home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / orca / default.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  151.7 KB  |  4,787 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''The default Script for presenting information to the user using
  5. both speech and Braille.  This is based primarily on the de-facto
  6. standard implementation of the AT-SPI, which is the GAIL support
  7. for GTK.'''
  8. __id__ = '$Id: default.py 4675 2009-04-11 22:43:39Z wwalker $'
  9. __version__ = '$Revision: 4675 $'
  10. __date__ = '$Date: 2009-04-11 18:43:39 -0400 (Sat, 11 Apr 2009) $'
  11. __copyright__ = 'Copyright (c) 2005-2008 Sun Microsystems Inc.'
  12. __license__ = 'LGPL'
  13. import locale
  14. import math
  15. import sys
  16. import time
  17. import pyatspi
  18. import braille
  19. import chnames
  20. import debug
  21. import find
  22. import flat_review
  23. import input_event
  24. import keybindings
  25. import mag
  26. import outline
  27. import orca
  28. import orca_prefs
  29. import orca_state
  30. import phonnames
  31. import pronunciation_dict
  32. import punctuation_settings
  33. import rolenames
  34. import script
  35. import settings
  36. import speech
  37. import speechserver
  38. import mouse_review
  39. import text_attribute_names
  40. from orca_i18n import _
  41. from orca_i18n import ngettext
  42. from orca_i18n import C_
  43.  
  44. class Script(script.Script):
  45.     EMBEDDED_OBJECT_CHARACTER = u'Ôøº'
  46.     NO_BREAK_SPACE_CHARACTER = u'¬†'
  47.     
  48.     def __init__(self, app):
  49.         '''Creates a new script for the given application.
  50.  
  51.         Arguments:
  52.         - app: the application to create a script for.
  53.         '''
  54.         script.Script.__init__(self, app)
  55.         self.flatReviewContext = None
  56.         self.windowActivateTime = None
  57.         self.lastReviewCurrentEvent = None
  58.         self.exitLearnModeKeyBinding = None
  59.         self.targetCursorCell = None
  60.         self.justEnteredFlatReviewMode = False
  61.         self.digits = '0123456789'
  62.         self.whitespace = ' \t\n\r\x0b\x0c'
  63.         self.lastWhereAmIEvent = None
  64.         self.lastSayAllEvent = None
  65.         self._unicodeCurrencySymbols = []
  66.         self.lastProgressBarTime = { }
  67.         self.lastProgressBarValue = { }
  68.         self.lastSelectedMenu = None
  69.         self.attributeNamesDict = { }
  70.  
  71.     
  72.     def setupInputEventHandlers(self):
  73.         '''Defines InputEventHandler fields for this script that can be
  74.         called by the key and braille bindings.'''
  75.         self.inputEventHandlers['leftClickReviewItemHandler'] = input_event.InputEventHandler(Script.leftClickReviewItem, _('Performs left click on current flat review item.'))
  76.         self.inputEventHandlers['rightClickReviewItemHandler'] = input_event.InputEventHandler(Script.rightClickReviewItem, _('Performs right click on current flat review item.'))
  77.         self.inputEventHandlers['sayAllHandler'] = input_event.InputEventHandler(Script.sayAll, _('Speaks entire document.'))
  78.         self.inputEventHandlers['whereAmIBasicHandler'] = input_event.InputEventHandler(Script.whereAmIBasic, _('Performs the basic where am I operation.'))
  79.         self.inputEventHandlers['whereAmIDetailedHandler'] = input_event.InputEventHandler(Script.whereAmIDetailed, _('Performs the detailed where am I operation.'))
  80.         self.inputEventHandlers['getTitleHandler'] = input_event.InputEventHandler(Script.getTitle, _('Speaks the title bar.'))
  81.         self.inputEventHandlers['getStatusBarHandler'] = input_event.InputEventHandler(Script.getStatusBar, _('Speaks the status bar.'))
  82.         self.inputEventHandlers['findHandler'] = input_event.InputEventHandler(orca.showFindGUI, _('Opens the Orca Find dialog.'))
  83.         self.inputEventHandlers['findNextHandler'] = input_event.InputEventHandler(Script.findNext, _('Searches for the next instance of a string.'))
  84.         self.inputEventHandlers['findPreviousHandler'] = input_event.InputEventHandler(Script.findPrevious, _('Searches for the previous instance of a string.'))
  85.         self.inputEventHandlers['showZonesHandler'] = input_event.InputEventHandler(Script.showZones, _('Paints and prints the visible zones in the active window.'))
  86.         self.inputEventHandlers['toggleFlatReviewModeHandler'] = input_event.InputEventHandler(Script.toggleFlatReviewMode, _('Enters and exits flat review mode.'))
  87.         self.inputEventHandlers['reviewPreviousLineHandler'] = input_event.InputEventHandler(Script.reviewPreviousLine, _('Moves flat review to the beginning of the previous line.'))
  88.         self.inputEventHandlers['reviewHomeHandler'] = input_event.InputEventHandler(Script.reviewHome, _('Moves flat review to the home position.'))
  89.         self.inputEventHandlers['reviewCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewCurrentLine, _('Speaks the current flat review line.'))
  90.         self.inputEventHandlers['reviewSpellCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentLine, _('Spells the current flat review line.'))
  91.         self.inputEventHandlers['reviewPhoneticCurrentLineHandler'] = input_event.InputEventHandler(Script.reviewPhoneticCurrentLine, _('Phonetically spells the current flat review line.'))
  92.         self.inputEventHandlers['reviewNextLineHandler'] = input_event.InputEventHandler(Script.reviewNextLine, _('Moves flat review to the beginning of the next line.'))
  93.         self.inputEventHandlers['reviewEndHandler'] = input_event.InputEventHandler(Script.reviewEnd, _('Moves flat review to the end position.'))
  94.         self.inputEventHandlers['reviewPreviousItemHandler'] = input_event.InputEventHandler(Script.reviewPreviousItem, _('Moves flat review to the previous item or word.'))
  95.         self.inputEventHandlers['reviewAboveHandler'] = input_event.InputEventHandler(Script.reviewAbove, _('Moves flat review to the word above the current word.'))
  96.         self.inputEventHandlers['reviewCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewCurrentItem, _('Speaks the current flat review item or word.'))
  97.         self.inputEventHandlers['reviewSpellCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentItem, _('Spells the current flat review item or word.'))
  98.         self.inputEventHandlers['reviewPhoneticCurrentItemHandler'] = input_event.InputEventHandler(Script.reviewPhoneticCurrentItem, _('Phonetically spells the current flat review item or word.'))
  99.         self.inputEventHandlers['reviewCurrentAccessibleHandler'] = input_event.InputEventHandler(Script.reviewCurrentAccessible, _('Speaks the current flat review object.'))
  100.         self.inputEventHandlers['reviewNextItemHandler'] = input_event.InputEventHandler(Script.reviewNextItem, _('Moves flat review to the next item or word.'))
  101.         self.inputEventHandlers['reviewBelowHandler'] = input_event.InputEventHandler(Script.reviewBelow, _('Moves flat review to the word below the current word.'))
  102.         self.inputEventHandlers['reviewPreviousCharacterHandler'] = input_event.InputEventHandler(Script.reviewPreviousCharacter, _('Moves flat review to the previous character.'))
  103.         self.inputEventHandlers['reviewEndOfLineHandler'] = input_event.InputEventHandler(Script.reviewEndOfLine, _('Moves flat review to the end of the line.'))
  104.         self.inputEventHandlers['reviewCurrentCharacterHandler'] = input_event.InputEventHandler(Script.reviewCurrentCharacter, _('Speaks the current flat review character.'))
  105.         self.inputEventHandlers['reviewSpellCurrentCharacterHandler'] = input_event.InputEventHandler(Script.reviewSpellCurrentCharacter, _('Phonetically speaks the current flat review character.'))
  106.         self.inputEventHandlers['reviewNextCharacterHandler'] = input_event.InputEventHandler(Script.reviewNextCharacter, _('Moves flat review to the next character.'))
  107.         self.inputEventHandlers['toggleTableCellReadModeHandler'] = input_event.InputEventHandler(Script.toggleTableCellReadMode, _('Toggles whether to read just the current table cell or the whole row.'))
  108.         self.inputEventHandlers['readCharAttributesHandler'] = input_event.InputEventHandler(Script.readCharAttributes, _('Reads the attributes associated with the current text character.'))
  109.         self.inputEventHandlers['reportScriptInfoHandler'] = input_event.InputEventHandler(Script.reportScriptInfo, _('Reports information on current script.'))
  110.         self.inputEventHandlers['panBrailleLeftHandler'] = input_event.InputEventHandler(Script.panBrailleLeft, _('Pans the braille display to the left.'), False)
  111.         self.inputEventHandlers['panBrailleRightHandler'] = input_event.InputEventHandler(Script.panBrailleRight, _('Pans the braille display to the right.'), False)
  112.         self.inputEventHandlers['reviewBottomLeftHandler'] = input_event.InputEventHandler(Script.reviewBottomLeft, _('Moves flat review to the bottom left.'))
  113.         self.inputEventHandlers['goBrailleHomeHandler'] = input_event.InputEventHandler(Script.goBrailleHome, _('Returns to object with keyboard focus.'))
  114.         self.inputEventHandlers['enterLearnModeHandler'] = input_event.InputEventHandler(Script.enterLearnMode, _('Enters learn mode.  Press escape to exit learn mode.'))
  115.         self.inputEventHandlers['decreaseSpeechRateHandler'] = input_event.InputEventHandler(speech.decreaseSpeechRate, _('Decreases the speech rate.'))
  116.         self.inputEventHandlers['increaseSpeechRateHandler'] = input_event.InputEventHandler(speech.increaseSpeechRate, _('Increases the speech rate.'))
  117.         self.inputEventHandlers['decreaseSpeechPitchHandler'] = input_event.InputEventHandler(speech.decreaseSpeechPitch, _('Decreases the speech pitch.'))
  118.         self.inputEventHandlers['increaseSpeechPitchHandler'] = input_event.InputEventHandler(speech.increaseSpeechPitch, _('Increases the speech pitch.'))
  119.         self.inputEventHandlers['shutdownHandler'] = input_event.InputEventHandler(orca.quitOrca, _('Quits Orca'))
  120.         self.inputEventHandlers['preferencesSettingsHandler'] = input_event.InputEventHandler(orca.showPreferencesGUI, _('Displays the preferences configuration dialog.'))
  121.         self.inputEventHandlers['appPreferencesSettingsHandler'] = input_event.InputEventHandler(orca.showAppPreferencesGUI, _('Displays the application preferences configuration dialog.'))
  122.         self.inputEventHandlers['toggleSilenceSpeechHandler'] = input_event.InputEventHandler(orca.toggleSilenceSpeech, _('Toggles the silencing of speech.'))
  123.         self.inputEventHandlers['listAppsHandler'] = input_event.InputEventHandler(Script.printAppsHandler, _('Prints a debug listing of all known applications to the console where Orca is running.'))
  124.         self.inputEventHandlers['cycleDebugLevelHandler'] = input_event.InputEventHandler(orca.cycleDebugLevel, _('Cycles the debug level at run time.'))
  125.         self.inputEventHandlers['printActiveAppHandler'] = input_event.InputEventHandler(Script.printActiveAppHandler, _('Prints debug information about the currently active application to the console where Orca is running.'))
  126.         self.inputEventHandlers['printAncestryHandler'] = input_event.InputEventHandler(Script.printAncestryHandler, _('Prints debug information about the ancestry of the object with focus.'))
  127.         self.inputEventHandlers['printHierarchyHandler'] = input_event.InputEventHandler(Script.printHierarchyHandler, _('Prints debug information about the application with focus.'))
  128.         self.inputEventHandlers['printMemoryUsageHandler'] = input_event.InputEventHandler(Script.printMemoryUsageHandler, _('Prints memory usage information.'))
  129.         self.inputEventHandlers['bookmarkCurrentWhereAmI'] = input_event.InputEventHandler(Script.bookmarkCurrentWhereAmI, _('Bookmark where am I with respect to current position.'))
  130.         self.inputEventHandlers['goToBookmark'] = input_event.InputEventHandler(Script.goToBookmark, _('Go to bookmark.'))
  131.         self.inputEventHandlers['addBookmark'] = input_event.InputEventHandler(Script.addBookmark, _('Add bookmark.'))
  132.         self.inputEventHandlers['saveBookmarks'] = input_event.InputEventHandler(Script.saveBookmarks, _('Save bookmarks.'))
  133.         self.inputEventHandlers['goToNextBookmark'] = input_event.InputEventHandler(Script.goToNextBookmark, _('Go to next bookmark location.'))
  134.         self.inputEventHandlers['goToPrevBookmark'] = input_event.InputEventHandler(Script.goToPrevBookmark, _('Go to previous bookmark location.'))
  135.         self.inputEventHandlers['toggleColorEnhancementsHandler'] = input_event.InputEventHandler(mag.toggleColorEnhancements, _('Toggles color enhancements.'))
  136.         self.inputEventHandlers['toggleMouseEnhancementsHandler'] = input_event.InputEventHandler(mag.toggleMouseEnhancements, _('Toggles mouse enhancements.'))
  137.         self.inputEventHandlers['increaseMagnificationHandler'] = input_event.InputEventHandler(mag.increaseMagnification, _('Increases the magnification level.'))
  138.         self.inputEventHandlers['decreaseMagnificationHandler'] = input_event.InputEventHandler(mag.decreaseMagnification, _('Decreases the magnification level.'))
  139.         self.inputEventHandlers['toggleMagnifierHandler'] = input_event.InputEventHandler(mag.toggleMagnifier, _('Toggles the magnifier.'))
  140.         self.inputEventHandlers['cycleZoomerTypeHandler'] = input_event.InputEventHandler(mag.cycleZoomerType, _('Cycles to the next magnifier position.'))
  141.         self.inputEventHandlers['toggleMouseReviewHandler'] = input_event.InputEventHandler(mouse_review.toggle, _('Toggle mouse review mode.'))
  142.         self.inputEventHandlers['bypassNextCommandHandler'] = input_event.InputEventHandler(Script.bypassNextCommand, _('Passes the next command on to the current application.'))
  143.  
  144.     
  145.     def getInputEventHandlerKey(self, inputEventHandler):
  146.         '''Returns the name of the key that contains an inputEventHadler
  147.         passed as argument
  148.         '''
  149.         for keyName, handler in self.inputEventHandlers.iteritems():
  150.             if handler == inputEventHandler:
  151.                 return keyName
  152.         
  153.  
  154.     
  155.     def getListeners(self):
  156.         '''Sets up the AT-SPI event listeners for this script.
  157.         '''
  158.         listeners = script.Script.getListeners(self)
  159.         listeners['focus:'] = self.onFocus
  160.         listeners['mouse:button'] = self.onMouseButton
  161.         listeners['object:property-change:accessible-name'] = self.onNameChanged
  162.         listeners['object:text-caret-moved'] = self.onCaretMoved
  163.         listeners['object:text-changed:delete'] = self.onTextDeleted
  164.         listeners['object:text-changed:insert'] = self.onTextInserted
  165.         listeners['object:active-descendant-changed'] = self.onActiveDescendantChanged
  166.         listeners['object:link-selected'] = self.onLinkSelected
  167.         listeners['object:state-changed:active'] = self.onStateChanged
  168.         listeners['object:state-changed:focused'] = self.onStateChanged
  169.         listeners['object:state-changed:showing'] = self.onStateChanged
  170.         listeners['object:state-changed:checked'] = self.onStateChanged
  171.         listeners['object:state-changed:pressed'] = self.onStateChanged
  172.         listeners['object:state-changed:indeterminate'] = self.onStateChanged
  173.         listeners['object:state-changed:expanded'] = self.onStateChanged
  174.         listeners['object:state-changed:selected'] = self.onStateChanged
  175.         listeners['object:text-selection-changed'] = self.onTextSelectionChanged
  176.         listeners['object:selection-changed'] = self.onSelectionChanged
  177.         listeners['object:property-change:accessible-value'] = self.onValueChanged
  178.         listeners['object:value-changed'] = self.onValueChanged
  179.         listeners['window:activate'] = self.onWindowActivated
  180.         listeners['window:deactivate'] = self.onWindowDeactivated
  181.         listeners['window:create'] = self.noOp
  182.         return listeners
  183.  
  184.     
  185.     def __getDesktopBindings(self):
  186.         '''Returns an instance of keybindings.KeyBindings that use the
  187.         numeric keypad for focus tracking and flat review.
  188.         '''
  189.         keyBindings = keybindings.KeyBindings()
  190.         keyBindings.add(keybindings.KeyBinding('KP_Divide', settings.NO_MODIFIER_MASK, settings.NO_MODIFIER_MASK, self.inputEventHandlers['leftClickReviewItemHandler']))
  191.         keyBindings.add(keybindings.KeyBinding('KP_Multiply', settings.NO_MODIFIER_MASK, settings.NO_MODIFIER_MASK, self.inputEventHandlers['rightClickReviewItemHandler']))
  192.         keyBindings.add(keybindings.KeyBinding('KP_Subtract', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleFlatReviewModeHandler']))
  193.         keyBindings.add(keybindings.KeyBinding('KP_Add', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['sayAllHandler']))
  194.         keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['whereAmIBasicHandler'], 1))
  195.         keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['whereAmIDetailedHandler'], 2))
  196.         keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getTitleHandler'], 1))
  197.         keyBindings.add(keybindings.KeyBinding('KP_Enter', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getStatusBarHandler'], 2))
  198.         keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['findHandler']))
  199.         keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findNextHandler']))
  200.         keyBindings.add(keybindings.KeyBinding('KP_Delete', settings.defaultModifierMask, settings.ORCA_SHIFT_MODIFIER_MASK, self.inputEventHandlers['findPreviousHandler']))
  201.         keyBindings.add(keybindings.KeyBinding('KP_7', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler']))
  202.         keyBindings.add(keybindings.KeyBinding('KP_Home', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler']))
  203.         keyBindings.add(keybindings.KeyBinding('KP_7', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler']))
  204.         keyBindings.add(keybindings.KeyBinding('KP_Home', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler']))
  205.         keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1))
  206.         keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2))
  207.         keyBindings.add(keybindings.KeyBinding('KP_8', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3))
  208.         keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1))
  209.         keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2))
  210.         keyBindings.add(keybindings.KeyBinding('KP_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3))
  211.         keyBindings.add(keybindings.KeyBinding('KP_9', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler']))
  212.         keyBindings.add(keybindings.KeyBinding('KP_Page_Up', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler']))
  213.         keyBindings.add(keybindings.KeyBinding('KP_9', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler']))
  214.         keyBindings.add(keybindings.KeyBinding('KP_Page_Up', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler']))
  215.         keyBindings.add(keybindings.KeyBinding('KP_4', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler']))
  216.         keyBindings.add(keybindings.KeyBinding('KP_Left', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler']))
  217.         keyBindings.add(keybindings.KeyBinding('KP_4', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler']))
  218.         keyBindings.add(keybindings.KeyBinding('KP_Left', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler']))
  219.         keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1))
  220.         keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2))
  221.         keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3))
  222.         keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1))
  223.         keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2))
  224.         keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3))
  225.         keyBindings.add(keybindings.KeyBinding('KP_5', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler']))
  226.         keyBindings.add(keybindings.KeyBinding('KP_Begin', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler']))
  227.         keyBindings.add(keybindings.KeyBinding('KP_6', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler']))
  228.         keyBindings.add(keybindings.KeyBinding('KP_Right', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler']))
  229.         keyBindings.add(keybindings.KeyBinding('KP_6', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler']))
  230.         keyBindings.add(keybindings.KeyBinding('KP_Right', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler']))
  231.         keyBindings.add(keybindings.KeyBinding('KP_1', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler']))
  232.         keyBindings.add(keybindings.KeyBinding('KP_End', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler']))
  233.         keyBindings.add(keybindings.KeyBinding('KP_1', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler']))
  234.         keyBindings.add(keybindings.KeyBinding('KP_End', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler']))
  235.         keyBindings.add(keybindings.KeyBinding('KP_2', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1))
  236.         keyBindings.add(keybindings.KeyBinding('KP_2', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2))
  237.         keyBindings.add(keybindings.KeyBinding('KP_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1))
  238.         keyBindings.add(keybindings.KeyBinding('KP_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2))
  239.         keyBindings.add(keybindings.KeyBinding('KP_3', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler']))
  240.         keyBindings.add(keybindings.KeyBinding('KP_Page_Down', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler']))
  241.         return keyBindings
  242.  
  243.     
  244.     def __getLaptopBindings(self):
  245.         '''Returns an instance of keybindings.KeyBindings that use the
  246.         the main keyboard keys for focus tracking and flat review.
  247.         '''
  248.         keyBindings = keybindings.KeyBindings()
  249.         keyBindings.add(keybindings.KeyBinding('7', settings.ORCA_MODIFIER_MASK, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['leftClickReviewItemHandler']))
  250.         keyBindings.add(keybindings.KeyBinding('8', settings.ORCA_MODIFIER_MASK, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['rightClickReviewItemHandler']))
  251.         keyBindings.add(keybindings.KeyBinding('p', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleFlatReviewModeHandler']))
  252.         keyBindings.add(keybindings.KeyBinding('semicolon', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['sayAllHandler']))
  253.         keyBindings.add(keybindings.KeyBinding('Return', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['whereAmIBasicHandler'], 1))
  254.         keyBindings.add(keybindings.KeyBinding('Return', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['whereAmIDetailedHandler'], 2))
  255.         keyBindings.add(keybindings.KeyBinding('slash', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getTitleHandler'], 1))
  256.         keyBindings.add(keybindings.KeyBinding('slash', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['getStatusBarHandler'], 2))
  257.         keyBindings.add(keybindings.KeyBinding('bracketleft', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findHandler']))
  258.         keyBindings.add(keybindings.KeyBinding('bracketright', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['findNextHandler']))
  259.         keyBindings.add(keybindings.KeyBinding('bracketright', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['findPreviousHandler']))
  260.         keyBindings.add(keybindings.KeyBinding('u', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousLineHandler']))
  261.         keyBindings.add(keybindings.KeyBinding('u', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewHomeHandler']))
  262.         keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentLineHandler'], 1))
  263.         keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentLineHandler'], 2))
  264.         keyBindings.add(keybindings.KeyBinding('i', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentLineHandler'], 3))
  265.         keyBindings.add(keybindings.KeyBinding('o', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextLineHandler']))
  266.         keyBindings.add(keybindings.KeyBinding('o', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewEndHandler']))
  267.         keyBindings.add(keybindings.KeyBinding('j', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousItemHandler']))
  268.         keyBindings.add(keybindings.KeyBinding('j', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewAboveHandler']))
  269.         keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentItemHandler'], 1))
  270.         keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentItemHandler'], 2))
  271.         keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPhoneticCurrentItemHandler'], 3))
  272.         keyBindings.add(keybindings.KeyBinding('k', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentAccessibleHandler']))
  273.         keyBindings.add(keybindings.KeyBinding('l', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextItemHandler']))
  274.         keyBindings.add(keybindings.KeyBinding('l', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewBelowHandler']))
  275.         keyBindings.add(keybindings.KeyBinding('m', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewPreviousCharacterHandler']))
  276.         keyBindings.add(keybindings.KeyBinding('m', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['reviewEndOfLineHandler']))
  277.         keyBindings.add(keybindings.KeyBinding('comma', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewCurrentCharacterHandler'], 1))
  278.         keyBindings.add(keybindings.KeyBinding('comma', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewSpellCurrentCharacterHandler'], 2))
  279.         keyBindings.add(keybindings.KeyBinding('period', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['reviewNextCharacterHandler']))
  280.         return keyBindings
  281.  
  282.     
  283.     def getKeyBindings(self):
  284.         '''Defines the key bindings for this script.
  285.  
  286.         Returns an instance of keybindings.KeyBindings.
  287.         '''
  288.         keyBindings = script.Script.getKeyBindings(self)
  289.         if settings.keyboardLayout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP:
  290.             for keyBinding in self._Script__getDesktopBindings().keyBindings:
  291.                 keyBindings.add(keyBinding)
  292.             
  293.         else:
  294.             for keyBinding in self._Script__getLaptopBindings().keyBindings:
  295.                 keyBindings.add(keyBinding)
  296.             
  297.         keyBindings.add(keybindings.KeyBinding('Num_Lock', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['showZonesHandler']))
  298.         keyBindings.add(keybindings.KeyBinding('F11', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleTableCellReadModeHandler']))
  299.         keyBindings.add(keybindings.KeyBinding('SunF36', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleTableCellReadModeHandler']))
  300.         keyBindings.add(keybindings.KeyBinding('f', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['readCharAttributesHandler']))
  301.         keyBindings.add(keybindings.KeyBinding('h', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['enterLearnModeHandler']))
  302.         keyBindings.add(keybindings.KeyBinding('q', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['shutdownHandler']))
  303.         keyBindings.add(keybindings.KeyBinding('space', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['preferencesSettingsHandler']))
  304.         keyBindings.add(keybindings.KeyBinding('space', settings.defaultModifierMask, settings.ORCA_CTRL_MODIFIER_MASK, self.inputEventHandlers['appPreferencesSettingsHandler']))
  305.         keyBindings.add(keybindings.KeyBinding('s', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['toggleSilenceSpeechHandler']))
  306.         keyBindings.add(keybindings.KeyBinding('End', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['listAppsHandler']))
  307.         keyBindings.add(keybindings.KeyBinding('Home', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['reportScriptInfoHandler']))
  308.         keyBindings.add(keybindings.KeyBinding('Page_Up', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['printAncestryHandler']))
  309.         keyBindings.add(keybindings.KeyBinding('Page_Down', settings.defaultModifierMask, settings.ORCA_CTRL_ALT_MODIFIER_MASK, self.inputEventHandlers['printHierarchyHandler']))
  310.         keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_ALT_MODIFIER_MASK, self.inputEventHandlers['saveBookmarks']))
  311.         keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_SHIFT_MODIFIER_MASK, self.inputEventHandlers['goToPrevBookmark']))
  312.         keyBindings.add(keybindings.KeyBinding('b', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['goToNextBookmark']))
  313.         for key in xrange(1, 7):
  314.             keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.ORCA_ALT_MODIFIER_MASK, self.inputEventHandlers['addBookmark']))
  315.             keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['goToBookmark']))
  316.             keyBindings.add(keybindings.KeyBinding(str(key), settings.defaultModifierMask, settings.SHIFT_ALT_MODIFIER_MASK, self.inputEventHandlers['bookmarkCurrentWhereAmI']))
  317.         
  318.         keyBindings.add(keybindings.KeyBinding('BackSpace', settings.defaultModifierMask, settings.ORCA_MODIFIER_MASK, self.inputEventHandlers['bypassNextCommandHandler']))
  319.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['reportScriptInfoHandler']))
  320.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['cycleDebugLevelHandler']))
  321.         if settings.debugMemoryUsage:
  322.             keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['printMemoryUsageHandler']))
  323.         
  324.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseSpeechRateHandler']))
  325.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseSpeechRateHandler']))
  326.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseSpeechPitchHandler']))
  327.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseSpeechPitchHandler']))
  328.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleColorEnhancementsHandler']))
  329.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMouseEnhancementsHandler']))
  330.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['increaseMagnificationHandler']))
  331.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['decreaseMagnificationHandler']))
  332.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMagnifierHandler']))
  333.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['cycleZoomerTypeHandler']))
  334.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['panBrailleLeftHandler']))
  335.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['panBrailleRightHandler']))
  336.         keyBindings.add(keybindings.KeyBinding('', settings.defaultModifierMask, settings.NO_MODIFIER_MASK, self.inputEventHandlers['toggleMouseReviewHandler']))
  337.         keyBindings = settings.overrideKeyBindings(self, keyBindings)
  338.         return keyBindings
  339.  
  340.     
  341.     def getBrailleBindings(self):
  342.         '''Defines the braille bindings for this script.
  343.  
  344.         Returns a dictionary where the keys are BrlTTY commands and the
  345.         values are InputEventHandler instances.
  346.         '''
  347.         brailleBindings = script.Script.getBrailleBindings(self)
  348.         brailleBindings[braille.CMD_FWINLT] = self.inputEventHandlers['panBrailleLeftHandler']
  349.         brailleBindings[braille.CMD_FWINRT] = self.inputEventHandlers['panBrailleRightHandler']
  350.         brailleBindings[braille.CMD_LNUP] = self.inputEventHandlers['reviewAboveHandler']
  351.         brailleBindings[braille.CMD_LNDN] = self.inputEventHandlers['reviewBelowHandler']
  352.         brailleBindings[braille.CMD_FREEZE] = self.inputEventHandlers['toggleFlatReviewModeHandler']
  353.         brailleBindings[braille.CMD_TOP_LEFT] = self.inputEventHandlers['reviewHomeHandler']
  354.         brailleBindings[braille.CMD_BOT_LEFT] = self.inputEventHandlers['reviewBottomLeftHandler']
  355.         brailleBindings[braille.CMD_HOME] = self.inputEventHandlers['goBrailleHomeHandler']
  356.         return brailleBindings
  357.  
  358.     
  359.     def processKeyboardEvent(self, keyboardEvent):
  360.         '''Processes the given keyboard event. It uses the super
  361.         class equivalent to do most of the work. The only thing done here
  362.         is to detect when the user is trying to get out of learn mode.
  363.  
  364.         Arguments:
  365.         - keyboardEvent: an instance of input_event.KeyboardEvent
  366.         '''
  367.         return script.Script.processKeyboardEvent(self, keyboardEvent)
  368.  
  369.     
  370.     def __sayAllProgressCallback(self, context, progressType):
  371.         text = context.obj.queryText()
  372.         if progressType == speechserver.SayAllContext.PROGRESS:
  373.             return None
  374.         if progressType == speechserver.SayAllContext.INTERRUPTED:
  375.             text.setCaretOffset(context.currentOffset)
  376.         elif progressType == speechserver.SayAllContext.COMPLETED:
  377.             orca.setLocusOfFocus(None, context.obj, False)
  378.             text.setCaretOffset(context.currentOffset)
  379.         
  380.         if text.getNSelections():
  381.             text.setSelection(0, context.currentOffset, context.currentOffset)
  382.         
  383.  
  384.     
  385.     def sayAll(self, inputEvent):
  386.         clickCount = self.getClickCount()
  387.         doubleClick = clickCount == 2
  388.         self.lastSayAllEvent = inputEvent
  389.         if doubleClick:
  390.             context = self.getFlatReviewContext()
  391.             utterances = []
  392.             context.goBegin()
  393.             while True:
  394.                 (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.ZONE)
  395.                 utterances.append(wordString)
  396.                 moved = context.goNext(flat_review.Context.ZONE, flat_review.Context.WRAP_LINE)
  397.                 if not moved:
  398.                     break
  399.                     continue
  400.             speech.speakUtterances(utterances)
  401.         elif self.isTextArea(orca_state.locusOfFocus):
  402.             
  403.             try:
  404.                 orca_state.locusOfFocus.queryText()
  405.             except NotImplementedError:
  406.                 utterances = self.speechGenerator.getSpeech(orca_state.locusOfFocus, False)
  407.                 utterances.extend(self.tutorialGenerator.getTutorial(orca_state.locusOfFocus, False))
  408.                 speech.speakUtterances(utterances)
  409.             except AttributeError:
  410.                 pass
  411.  
  412.             speech.sayAll(self.textLines(orca_state.locusOfFocus), self._Script__sayAllProgressCallback)
  413.         
  414.         return True
  415.  
  416.     
  417.     def isTextArea(self, obj):
  418.         '''Returns True if obj is a GUI component that is for entering text.
  419.  
  420.         Arguments:
  421.         - obj: an accessible
  422.         '''
  423.         if obj:
  424.             pass
  425.         return obj.getRole() in (pyatspi.ROLE_TEXT, pyatspi.ROLE_PARAGRAPH, pyatspi.ROLE_TERMINAL)
  426.  
  427.     
  428.     def isReadOnlyTextArea(self, obj):
  429.         '''Returns True if obj is a text entry area that is read only.'''
  430.         state = obj.getState()
  431.         if self.isTextArea(obj) and state.contains(pyatspi.STATE_FOCUSABLE):
  432.             pass
  433.         readOnly = not state.contains(pyatspi.STATE_EDITABLE)
  434.         debug.println(debug.LEVEL_ALL, 'default.py:isReadOnlyTextArea=%s for %s' % (readOnly, debug.getAccessibleDetails(obj)))
  435.         return readOnly
  436.  
  437.     
  438.     def getText(self, obj, startOffset, endOffset):
  439.         """Returns the substring of the given object's text specialization.
  440.  
  441.         Arguments:
  442.         - obj: an accessible supporting the accessible text specialization
  443.         - startOffset: the starting character position
  444.         - endOffset: the ending character position
  445.         """
  446.         return obj.queryText().getText(startOffset, endOffset)
  447.  
  448.     
  449.     def sayPhrase(self, obj, startOffset, endOffset):
  450.         """Speaks the text of an Accessible object between the start and
  451.         end offsets, unless the phrase is empty in which case it's ignored.
  452.  
  453.         Arguments:
  454.         - obj: an Accessible object that implements the AccessibleText
  455.                interface
  456.         - startOffset: the start text offset.
  457.         - endOffset: the end text offset.
  458.         """
  459.         phrase = self.getText(obj, startOffset, endOffset)
  460.         if len(phrase) and phrase != '\n':
  461.             if phrase.isupper():
  462.                 voice = self.voices[settings.UPPERCASE_VOICE]
  463.             else:
  464.                 voice = self.voices[settings.DEFAULT_VOICE]
  465.             phrase = self.adjustForRepeats(phrase)
  466.             speech.speak(phrase, voice)
  467.         else:
  468.             self.sayCharacter(obj)
  469.  
  470.     
  471.     def sayLine(self, obj):
  472.         """Speaks the line of an AccessibleText object that contains the
  473.         caret, unless the line is empty in which case it's ignored.
  474.  
  475.         Arguments:
  476.         - obj: an Accessible object that implements the AccessibleText
  477.                interface
  478.         """
  479.         (line, caretOffset, startOffset) = self.getTextLineAtCaret(obj)
  480.         debug.println(debug.LEVEL_FINEST, 'sayLine: line=<%s>, len=%d, start=%d, ' % (line, len(line), startOffset))
  481.         debug.println(debug.LEVEL_FINEST, 'caret=%d, speakBlankLines=%s' % (caretOffset, settings.speakBlankLines))
  482.         if len(line) and line != '\n':
  483.             if line.isupper():
  484.                 voice = self.voices[settings.UPPERCASE_VOICE]
  485.             else:
  486.                 voice = self.voices[settings.DEFAULT_VOICE]
  487.             if settings.enableSpeechIndentation:
  488.                 self.speakTextIndentation(obj, line)
  489.             
  490.             line = self.adjustForLinks(obj, line, startOffset)
  491.             line = self.adjustForRepeats(line)
  492.             speech.speak(line, voice)
  493.         else:
  494.             self.sayCharacter(obj)
  495.  
  496.     
  497.     def sayWord(self, obj):
  498.         '''Speaks the word at the caret.  [[[TODO: WDW - what if there is no
  499.         word at the caret?]]]
  500.  
  501.         Arguments:
  502.         - obj: an Accessible object that implements the AccessibleText
  503.                interface
  504.         '''
  505.         text = obj.queryText()
  506.         offset = text.caretOffset
  507.         lastKey = orca_state.lastNonModifierKeyEvent.event_string
  508.         lastWord = orca_state.lastWord
  509.         (word, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_WORD_START)
  510.         if lastKey == 'Right' and len(lastWord) > 0:
  511.             lastChar = lastWord[len(lastWord) - 1]
  512.             if lastChar == '\n' and lastWord != word:
  513.                 voice = self.voices[settings.DEFAULT_VOICE]
  514.                 speech.speakCharacter('\n', voice)
  515.             
  516.         
  517.         if lastKey == 'Left' and len(word) > 0:
  518.             lastChar = word[len(word) - 1]
  519.             if lastChar == '\n' and lastWord != word:
  520.                 voice = self.voices[settings.DEFAULT_VOICE]
  521.                 speech.speakCharacter('\n', voice)
  522.             
  523.         
  524.         if self.getLinkIndex(obj, offset) >= 0:
  525.             voice = self.voices[settings.HYPERLINK_VOICE]
  526.         elif word.isupper():
  527.             voice = self.voices[settings.UPPERCASE_VOICE]
  528.         else:
  529.             voice = self.voices[settings.DEFAULT_VOICE]
  530.         word = self.adjustForRepeats(word)
  531.         orca_state.lastWord = word
  532.         speech.speak(word, voice)
  533.  
  534.     
  535.     def speakTextIndentation(self, obj, line):
  536.         '''Speaks a summary of the number of spaces and/or tabs at the
  537.         beginning of the given line.
  538.  
  539.         Arguments:
  540.         - obj: the text object.
  541.         - line: the string to check for spaces and tabs.
  542.         '''
  543.         line = line.replace('\xc2\xa0', ' ')
  544.         line = line.decode('UTF-8')
  545.         spaceCount = 0
  546.         tabCount = 0
  547.         utterance = ''
  548.         offset = 0
  549.         while True:
  550.             while offset < len(line) and line[offset] == ' ':
  551.                 spaceCount += 1
  552.                 offset += 1
  553.             if spaceCount:
  554.                 utterance += ngettext('%d space', '%d spaces', spaceCount) % spaceCount + ' '
  555.             
  556.             while offset < len(line) and line[offset] == '\t':
  557.                 tabCount += 1
  558.                 offset += 1
  559.             if tabCount:
  560.                 utterance += ngettext('%d tab', '%d tabs', tabCount) % tabCount + ' '
  561.             
  562.             if not spaceCount or tabCount:
  563.                 break
  564.             
  565.             spaceCount = tabCount = 0
  566.         if len(utterance):
  567.             speech.speak(utterance)
  568.         
  569.  
  570.     
  571.     def echoPreviousSentence(self, obj):
  572.         """Speaks the sentence prior to the caret, as long as there is
  573.         a sentence prior to the caret and there is no intervening sentence
  574.         delimiter between the caret and the end of the sentence.
  575.  
  576.         The entry condition for this method is that the character
  577.         prior to the current caret position is a sentence delimiter,
  578.         and it's what caused this method to be called in the first
  579.         place.
  580.  
  581.         Arguments:
  582.         - obj: an Accessible object that implements the AccessibleText
  583.         interface.
  584.         """
  585.         
  586.         try:
  587.             text = obj.queryText()
  588.         except NotImplementedError:
  589.             return None
  590.  
  591.         offset = text.caretOffset - 1
  592.         previousOffset = text.caretOffset - 2
  593.         if offset < 0 or previousOffset < 0:
  594.             return None
  595.         (currentChar, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR)
  596.         (previousChar, startOffset, endOffset) = text.getTextAtOffset(previousOffset, pyatspi.TEXT_BOUNDARY_CHAR)
  597.         if not self.isSentenceDelimiter(currentChar, previousChar):
  598.             return None
  599.         sentenceEndOffset = text.caretOffset - 2
  600.         sentenceStartOffset = sentenceEndOffset
  601.         while sentenceStartOffset >= 0:
  602.             (currentChar, startOffset, endOffset) = text.getTextAtOffset(sentenceStartOffset, pyatspi.TEXT_BOUNDARY_CHAR)
  603.             (previousChar, startOffset, endOffset) = text.getTextAtOffset(sentenceStartOffset - 1, pyatspi.TEXT_BOUNDARY_CHAR)
  604.             if self.isSentenceDelimiter(currentChar, previousChar):
  605.                 break
  606.                 continue
  607.             self.isSentenceDelimiter(currentChar, previousChar)
  608.             sentenceStartOffset -= 1
  609.             continue
  610.             previousOffset < 0
  611.         if sentenceStartOffset == sentenceEndOffset:
  612.             return None
  613.         sentence = self.getText(obj, sentenceStartOffset + 1, sentenceEndOffset + 1)
  614.         if self.getLinkIndex(obj, sentenceStartOffset + 1) >= 0:
  615.             voice = self.voices[settings.HYPERLINK_VOICE]
  616.         elif sentence.isupper():
  617.             voice = self.voices[settings.UPPERCASE_VOICE]
  618.         else:
  619.             voice = self.voices[settings.DEFAULT_VOICE]
  620.         sentence = self.adjustForRepeats(sentence)
  621.         speech.speak(sentence, voice)
  622.  
  623.     
  624.     def echoPreviousWord(self, obj, offset = None):
  625.         """Speaks the word prior to the caret, as long as there is
  626.         a word prior to the caret and there is no intervening word
  627.         delimiter between the caret and the end of the word.
  628.  
  629.         The entry condition for this method is that the character
  630.         prior to the current caret position is a word delimiter,
  631.         and it's what caused this method to be called in the first
  632.         place.
  633.  
  634.         Arguments:
  635.         - obj: an Accessible object that implements the AccessibleText
  636.                interface.
  637.         - offset: if not None, the offset within the text to use as the
  638.                   end of the word.
  639.         """
  640.         
  641.         try:
  642.             text = obj.queryText()
  643.         except NotImplementedError:
  644.             return None
  645.  
  646.         if not offset:
  647.             offset = text.caretOffset - 1
  648.         
  649.         if offset < 0:
  650.             return None
  651.         (char, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR)
  652.         if not self.isWordDelimiter(char):
  653.             return None
  654.         wordEndOffset = offset - 1
  655.         wordStartOffset = wordEndOffset
  656.         while wordStartOffset >= 0:
  657.             (char, startOffset, endOffset) = text.getTextAtOffset(wordStartOffset, pyatspi.TEXT_BOUNDARY_CHAR)
  658.             if self.isWordDelimiter(char):
  659.                 break
  660.                 continue
  661.             self.isWordDelimiter(char)
  662.             wordStartOffset -= 1
  663.             continue
  664.             offset < 0
  665.         if wordStartOffset == wordEndOffset:
  666.             return None
  667.         word = self.getText(obj, wordStartOffset + 1, wordEndOffset + 1)
  668.         if self.getLinkIndex(obj, wordStartOffset + 1) >= 0:
  669.             voice = self.voices[settings.HYPERLINK_VOICE]
  670.         elif word.isupper():
  671.             voice = self.voices[settings.UPPERCASE_VOICE]
  672.         else:
  673.             voice = self.voices[settings.DEFAULT_VOICE]
  674.         word = self.adjustForRepeats(word)
  675.         speech.speak(word, voice)
  676.  
  677.     
  678.     def sayCharacter(self, obj):
  679.         '''Speak the character at the caret.
  680.  
  681.         Arguments:
  682.         - obj: an Accessible object that implements the AccessibleText
  683.                interface
  684.         '''
  685.         text = obj.queryText()
  686.         offset = text.caretOffset
  687.         
  688.         try:
  689.             mods = orca_state.lastInputEvent.modifiers
  690.             eventString = orca_state.lastInputEvent.event_string
  691.         except:
  692.             mods = 0
  693.             eventString = ''
  694.  
  695.         if mods & settings.SHIFT_MODIFIER_MASK and eventString in ('Right', 'Down'):
  696.             offset -= 1
  697.         
  698.         (character, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR)
  699.         if not character:
  700.             character = '\n'
  701.         
  702.         if self.getLinkIndex(obj, offset) >= 0:
  703.             voice = self.voices[settings.HYPERLINK_VOICE]
  704.         elif character.decode('UTF-8').isupper():
  705.             voice = self.voices[settings.UPPERCASE_VOICE]
  706.         else:
  707.             voice = self.voices[settings.DEFAULT_VOICE]
  708.         debug.println(debug.LEVEL_FINEST, 'sayCharacter: char=<%s>, startOffset=%d, ' % (character, startOffset))
  709.         debug.println(debug.LEVEL_FINEST, 'caretOffset=%d, endOffset=%d, speakBlankLines=%s' % (offset, endOffset, settings.speakBlankLines))
  710.         if character == '\n':
  711.             line = text.getTextAtOffset(max(0, offset), pyatspi.TEXT_BOUNDARY_LINE_START)
  712.             if not line[0] or line[0] == '\n':
  713.                 if settings.speakBlankLines:
  714.                     speech.speak(_('blank'), voice, False)
  715.                 
  716.                 return None
  717.         
  718.         speech.speakCharacter(character, voice)
  719.  
  720.     
  721.     def isFunctionalDialog(self, obj):
  722.         '''Returns true if the window is a functioning as a dialog.
  723.         This method should be subclassed by application scripts as needed.
  724.         '''
  725.         return False
  726.  
  727.     
  728.     def getUnfocusedAlertAndDialogCount(self, obj):
  729.         '''If the current application has one or more alert or dialog
  730.         windows and the currently focused window is not an alert or a dialog,
  731.         return a count of the number of alert and dialog windows, otherwise
  732.         return a count of zero.
  733.  
  734.         Arguments:
  735.         - obj: the Accessible object
  736.  
  737.         Returns the alert and dialog count.
  738.         '''
  739.         alertAndDialogCount = 0
  740.         app = obj.getApplication()
  741.         window = self.getTopLevel(obj)
  742.         if window and window.getRole() != pyatspi.ROLE_ALERT and window.getRole() != pyatspi.ROLE_DIALOG and not self.isFunctionalDialog(window):
  743.             for child in app:
  744.                 if child.getRole() == pyatspi.ROLE_ALERT and child.getRole() == pyatspi.ROLE_DIALOG or self.isFunctionalDialog(child):
  745.                     alertAndDialogCount += 1
  746.                     continue
  747.             
  748.         
  749.         return alertAndDialogCount
  750.  
  751.     
  752.     def presentTooltip(self, obj):
  753.         '''
  754.         Speaks the tooltip for the current object of interest.
  755.         '''
  756.         text = ''
  757.         if obj.description:
  758.             text = obj.description
  759.         else:
  760.             text = self.whereAmI.getObjLabelAndName(obj)
  761.         debug.println(debug.LEVEL_FINEST, "presentTooltip: text='%s'" % text)
  762.         if text != '':
  763.             braille.displayMessage(text)
  764.             speech.speak(text)
  765.         
  766.  
  767.     
  768.     def doWhereAmI(self, inputEvent, basicOnly):
  769.         '''Peforms the whereAmI operation.
  770.  
  771.         Arguments:
  772.         - inputEvent:     The original inputEvent
  773.         '''
  774.         obj = orca_state.locusOfFocus
  775.         self.updateBraille(obj)
  776.         return self.whereAmI.whereAmI(obj, basicOnly)
  777.  
  778.     
  779.     def whereAmIBasic(self, inputEvent):
  780.         '''Speaks basic information about the current object of interest.
  781.         '''
  782.         self.doWhereAmI(inputEvent, True)
  783.  
  784.     
  785.     def whereAmIDetailed(self, inputEvent):
  786.         '''Speaks detailed/custom information about the current object of
  787.         interest.
  788.         '''
  789.         self.doWhereAmI(inputEvent, False)
  790.  
  791.     
  792.     def getTitle(self, inputEvent):
  793.         '''Speaks the title of the window with focus.
  794.         '''
  795.         obj = orca_state.locusOfFocus
  796.         self.updateBraille(obj)
  797.         return self.whereAmI.speakTitle(orca_state.locusOfFocus)
  798.  
  799.     
  800.     def getStatusBar(self, inputEvent):
  801.         '''Speaks the contents of the status bar of the window with focus.
  802.         '''
  803.         obj = orca_state.locusOfFocus
  804.         self.updateBraille(obj)
  805.         return self.whereAmI.speakStatusBar(orca_state.locusOfFocus)
  806.  
  807.     
  808.     def findCommonAncestor(self, a, b):
  809.         '''Finds the common ancestor between Accessible a and Accessible b.
  810.  
  811.         Arguments:
  812.         - a: Accessible
  813.         - b: Accessible
  814.         '''
  815.         debug.println(debug.LEVEL_FINEST, 'default.findCommonAncestor...')
  816.         if not a or not b:
  817.             return None
  818.         if a == b:
  819.             return a
  820.         aParents = [
  821.             a]
  822.         
  823.         try:
  824.             parent = a.parent
  825.             while parent and parent.parent != parent:
  826.                 aParents.append(parent)
  827.                 parent = parent.parent
  828.                 continue
  829.                 a == b
  830.             aParents.reverse()
  831.         except:
  832.             a == b
  833.             not b
  834.             debug.printException(debug.LEVEL_FINEST)
  835.  
  836.         bParents = [
  837.             b]
  838.         
  839.         try:
  840.             parent = b.parent
  841.             while parent and parent.parent != parent:
  842.                 bParents.append(parent)
  843.                 parent = parent.parent
  844.                 continue
  845.                 a == b
  846.             bParents.reverse()
  847.         except:
  848.             a == b
  849.             not b
  850.             debug.printException(debug.LEVEL_FINEST)
  851.  
  852.         commonAncestor = None
  853.         maxSearch = min(len(aParents), len(bParents))
  854.         i = 0
  855.         while i < maxSearch:
  856.             if self.isSameObject(aParents[i], bParents[i]):
  857.                 commonAncestor = aParents[i]
  858.                 i += 1
  859.                 continue
  860.             a == b
  861.             break
  862.             continue
  863.             not b
  864.         debug.println(debug.LEVEL_FINEST, '...default.findCommonAncestor')
  865.         return commonAncestor
  866.  
  867.     
  868.     def handleProgressBarUpdate(self, event, obj):
  869.         '''Determine whether this progress bar event should be spoken or not.
  870.         It should be spoken if:
  871.         1/ settings.enableProgressBarUpdates is True.
  872.         2/ The application with the progress bar has focus.
  873.         3/ The time of this event exceeds the
  874.            settings.progressBarUpdateInterval value.  This value
  875.            indicates the time (in seconds) between potential spoken
  876.            progress bar updates.
  877.         4/ The new value of the progress bar (converted to an integer),
  878.            is different from the last one or equals 100 (i.e complete).
  879.  
  880.         Arguments:
  881.         - event: if not None, the Event that caused this to happen
  882.         - obj:  the Accessible progress bar object.
  883.         '''
  884.         if settings.enableProgressBarUpdates:
  885.             if orca_state.locusOfFocus and orca_state.locusOfFocus.getApplication() == obj.getApplication():
  886.                 currentTime = time.time()
  887.                 defunctBars = 0
  888.                 mostRecentUpdate = [
  889.                     obj,
  890.                     0]
  891.                 for key, value in self.lastProgressBarTime.items():
  892.                     if value > mostRecentUpdate[1]:
  893.                         mostRecentUpdate = [
  894.                             key,
  895.                             value]
  896.                     
  897.                     
  898.                     try:
  899.                         isDefunct = key.getState().contains(pyatspi.STATE_DEFUNCT)
  900.                     except:
  901.                         isDefunct = True
  902.  
  903.                     if isDefunct:
  904.                         defunctBars += 1
  905.                         continue
  906.                 
  907.                 if defunctBars == len(self.lastProgressBarTime):
  908.                     self.lastProgressBarTime = { }
  909.                     self.lastProgressBarValue = { }
  910.                 
  911.                 if obj not in self.lastProgressBarTime:
  912.                     self.lastProgressBarTime[obj] = 0
  913.                 
  914.                 if obj not in self.lastProgressBarValue:
  915.                     self.lastProgressBarValue[obj] = None
  916.                 
  917.                 lastProgressBarTime = self.lastProgressBarTime[obj]
  918.                 lastProgressBarValue = self.lastProgressBarValue[obj]
  919.                 value = obj.queryValue()
  920.                 percentValue = int((value.currentValue / (value.maximumValue - value.minimumValue)) * 100)
  921.                 if currentTime - lastProgressBarTime > settings.progressBarUpdateInterval or percentValue == 100:
  922.                     if lastProgressBarValue != percentValue:
  923.                         utterances = []
  924.                         if len(self.lastProgressBarTime) > 1:
  925.                             index = 0
  926.                             for key in self.lastProgressBarTime.keys():
  927.                                 if key == obj and key != mostRecentUpdate[0]:
  928.                                     label = _('Progress bar %d.') % (index + 1)
  929.                                     utterances.append(label)
  930.                                     continue
  931.                                 index += 1
  932.                             
  933.                         
  934.                         percentage = _('%d percent.') % percentValue + ' '
  935.                         utterances.append(percentage)
  936.                         speech.speakUtterances(utterances)
  937.                         self.lastProgressBarTime[obj] = currentTime
  938.                         self.lastProgressBarValue[obj] = percentValue
  939.                     
  940.                 
  941.             
  942.         
  943.  
  944.     
  945.     def locusOfFocusChanged(self, event, oldLocusOfFocus, newLocusOfFocus):
  946.         '''Called when the visual object with focus changes.
  947.  
  948.         Arguments:
  949.         - event: if not None, the Event that caused the change
  950.         - oldLocusOfFocus: Accessible that is the old locus of focus
  951.         - newLocusOfFocus: Accessible that is the new locus of focus
  952.         '''
  953.         if newLocusOfFocus and newLocusOfFocus.getState().contains(pyatspi.STATE_DEFUNCT):
  954.             return None
  955.         
  956.         try:
  957.             if self.findCommandRun:
  958.                 return None
  959.         except:
  960.             newLocusOfFocus.getState().contains(pyatspi.STATE_DEFUNCT)
  961.  
  962.         if newLocusOfFocus:
  963.             mag.magnifyAccessible(event, newLocusOfFocus)
  964.         
  965.         if self.flatReviewContext:
  966.             self.toggleFlatReviewMode()
  967.         
  968.         if self.isSameObject(oldLocusOfFocus, newLocusOfFocus):
  969.             return None
  970.         if oldLocusOfFocus:
  971.             oldParent = oldLocusOfFocus.parent
  972.         else:
  973.             oldParent = None
  974.         if newLocusOfFocus:
  975.             newParent = newLocusOfFocus.parent
  976.         else:
  977.             newParent = None
  978.         if oldParent is not None and oldParent == newParent and newParent.getRole() == pyatspi.ROLE_TABLE:
  979.             for key in self.pointOfReference.keys():
  980.                 if key not in ('lastRow', 'lastColumn'):
  981.                     del self.pointOfReference[key]
  982.                     continue
  983.             
  984.         else:
  985.             self.pointOfReference = { }
  986.         if newLocusOfFocus:
  987.             self.updateBraille(newLocusOfFocus)
  988.             utterances = []
  989.             commonAncestor = self.findCommonAncestor(oldLocusOfFocus, newLocusOfFocus)
  990.             if commonAncestor:
  991.                 context = self.speechGenerator.getSpeechContext(newLocusOfFocus, commonAncestor)
  992.                 utterances.append(' '.join(context))
  993.             
  994.             oldNodeLevel = -1
  995.             newNodeLevel = -1
  996.             if newLocusOfFocus.getRole() == pyatspi.ROLE_TABLE_CELL or newParent.getRole() == pyatspi.ROLE_TABLE:
  997.                 
  998.                 try:
  999.                     table = oldParent.queryTable()
  1000.                 except:
  1001.                     table = None
  1002.  
  1003.                 if table:
  1004.                     if oldLocusOfFocus.getRole() == pyatspi.ROLE_TABLE_CELL or oldParent.getRole() == pyatspi.ROLE_TABLE:
  1005.                         index = self.getCellIndex(oldLocusOfFocus)
  1006.                         oldRow = table.getRowAtIndex(index)
  1007.                         oldCol = table.getColumnAtIndex(index)
  1008.                     else:
  1009.                         oldRow = -1
  1010.                         oldCol = -1
  1011.                 
  1012.                 try:
  1013.                     table = newParent.queryTable()
  1014.                 except:
  1015.                     pass
  1016.  
  1017.                 index = self.getCellIndex(newLocusOfFocus)
  1018.                 newRow = table.getRowAtIndex(index)
  1019.                 newCol = table.getColumnAtIndex(index)
  1020.                 if newRow >= 0:
  1021.                     if newRow != oldRow or oldParent != newParent:
  1022.                         desc = table.getRowDescription(newRow)
  1023.                         if not desc:
  1024.                             header = table.getRowHeader(newRow)
  1025.                             if header:
  1026.                                 desc = self.getDisplayedText(header)
  1027.                             
  1028.                         
  1029.                         if desc and len(desc):
  1030.                             text = desc
  1031.                             if settings.speechVerbosityLevel == settings.VERBOSITY_LEVEL_VERBOSE:
  1032.                                 text += ' ' + rolenames.rolenames[pyatspi.ROLE_ROW_HEADER].speech
  1033.                             
  1034.                             utterances.append(text)
  1035.                         
  1036.                     
  1037.                 if newCol >= 0:
  1038.                     pass
  1039.                 None if newCol != oldCol or oldParent != newParent else topName.endswith(' - Thunderbird')
  1040.             
  1041.             oldNodeLevel = self.getNodeLevel(oldLocusOfFocus)
  1042.             newNodeLevel = self.getNodeLevel(newLocusOfFocus)
  1043.             if newLocusOfFocus and newLocusOfFocus.getRole() == pyatspi.ROLE_RADIO_BUTTON:
  1044.                 radioGroupLabel = None
  1045.                 inSameGroup = False
  1046.                 relations = newLocusOfFocus.getRelationSet()
  1047.                 for relation in relations:
  1048.                     if not radioGroupLabel and relation.getRelationType() == pyatspi.RELATION_LABELLED_BY:
  1049.                         radioGroupLabel = relation.getTarget(0)
  1050.                     
  1051.                     if not inSameGroup and relation.getRelationType() == pyatspi.RELATION_MEMBER_OF:
  1052.                         for i in range(0, relation.getNTargets()):
  1053.                             target = relation.getTarget(i)
  1054.                             if target == oldLocusOfFocus:
  1055.                                 inSameGroup = True
  1056.                                 break
  1057.                                 continue
  1058.                         
  1059.                 
  1060.                 if not inSameGroup and radioGroupLabel:
  1061.                     utterances.append(self.getDisplayedText(radioGroupLabel))
  1062.                 
  1063.             
  1064.             rolesList = [
  1065.                 pyatspi.ROLE_TABLE_CELL,
  1066.                 pyatspi.ROLE_TABLE,
  1067.                 pyatspi.ROLE_SCROLL_PANE,
  1068.                 pyatspi.ROLE_PANEL,
  1069.                 pyatspi.ROLE_PANEL]
  1070.             if self.isDesiredFocusedItem(newLocusOfFocus, rolesList) and newLocusOfFocus.getApplication().name == 'orca':
  1071.                 orca_state.usePronunciationDictionary = False
  1072.             else:
  1073.                 orca_state.usePronunciationDictionary = True
  1074.             utterances.extend(self.speechGenerator.getSpeech(newLocusOfFocus, False))
  1075.             utterances.extend(self.tutorialGenerator.getTutorial(newLocusOfFocus, False))
  1076.             if oldNodeLevel != newNodeLevel and newNodeLevel >= 0:
  1077.                 utterances.append(_('tree level %d') % (newNodeLevel + 1))
  1078.             
  1079.             checkIfSelected = False
  1080.             (objRole, parentRole, state) = (None, None, None)
  1081.             if newLocusOfFocus:
  1082.                 objRole = newLocusOfFocus.getRole()
  1083.                 state = newLocusOfFocus.getState()
  1084.                 if newLocusOfFocus.parent:
  1085.                     parentRole = newLocusOfFocus.parent.getRole()
  1086.                 
  1087.             
  1088.             if objRole == pyatspi.ROLE_TABLE_CELL:
  1089.                 if parentRole == pyatspi.ROLE_TREE_TABLE or parentRole == pyatspi.ROLE_TABLE:
  1090.                     checkIfSelected = True
  1091.                 
  1092.             if checkIfSelected == True and orca_state.lastNonModifierKeyEvent:
  1093.                 if orca_state.lastNonModifierKeyEvent.event_string == 'Left' or orca_state.lastNonModifierKeyEvent.event_string == 'Right':
  1094.                     checkIfSelected = False
  1095.                 
  1096.             if objRole == pyatspi.ROLE_ICON and parentRole == pyatspi.ROLE_LAYERED_PANE:
  1097.                 checkIfSelected = True
  1098.             
  1099.             if checkIfSelected and state and not state.contains(pyatspi.STATE_SELECTED):
  1100.                 utterances.append(C_('tablecell', ' not selected'))
  1101.             
  1102.             if event and event.type.startswith('focus:') and self.windowActivateTime:
  1103.                 pass
  1104.             shouldNotInterrupt = time.time() - self.windowActivateTime < 1
  1105.             if objRole == pyatspi.ROLE_LINK:
  1106.                 voice = self.voices[settings.HYPERLINK_VOICE]
  1107.             else:
  1108.                 voice = self.voices[settings.DEFAULT_VOICE]
  1109.             speech.speakUtterances(utterances, voice, not shouldNotInterrupt)
  1110.             if objRole == pyatspi.ROLE_TABLE_CELL:
  1111.                 
  1112.                 try:
  1113.                     table = newParent.queryTable()
  1114.                 except:
  1115.                     pass
  1116.  
  1117.                 index = self.getCellIndex(newLocusOfFocus)
  1118.                 column = table.getColumnAtIndex(index)
  1119.                 self.pointOfReference['lastColumn'] = column
  1120.                 row = table.getRowAtIndex(index)
  1121.                 self.pointOfReference['lastRow'] = row
  1122.             
  1123.         else:
  1124.             orca_state.noFocusTimeStamp = time.time()
  1125.  
  1126.     
  1127.     def visualAppearanceChanged(self, event, obj):
  1128.         """Called when the visual appearance of an object changes.  This
  1129.         method should not be called for objects whose visual appearance
  1130.         changes solely because of focus -- setLocusOfFocus is used for that.
  1131.         Instead, it is intended mostly for objects whose notional 'value' has
  1132.         changed, such as a checkbox changing state, a progress bar advancing,
  1133.         a slider moving, text inserted, caret moved, etc.
  1134.  
  1135.         Arguments:
  1136.         - event: if not None, the Event that caused this to happen
  1137.         - obj: the Accessible whose visual appearance changed.
  1138.         """
  1139.         if obj.getRole() == pyatspi.ROLE_PROGRESS_BAR:
  1140.             self.handleProgressBarUpdate(event, obj)
  1141.         
  1142.         if self.flatReviewContext:
  1143.             if self.isSameObject(obj, self.flatReviewContext.getCurrentAccessible()):
  1144.                 self.updateBrailleReview()
  1145.             
  1146.             return None
  1147.         if False and obj.getRole() == pyatspi.ROLE_PANEL and event.detail1 == 1 and self.isInActiveApp(obj):
  1148.             reallyShowing = True
  1149.             parent = obj.parent
  1150.             while reallyShowing and parent and parent != parent.parent and parent.getRole() != pyatspi.ROLE_APPLICATION:
  1151.                 debug.println(debug.LEVEL_FINEST, 'default.visualAppearanceChanged - ' + 'checking parent')
  1152.                 reallyShowing = parent.getState().contains(pyatspi.STATE_SHOWING)
  1153.                 parent = parent.parent
  1154.                 continue
  1155.                 self.flatReviewContext
  1156.             if reallyShowing:
  1157.                 utterances = []
  1158.                 labels = self.findUnrelatedLabels(obj)
  1159.                 for label in labels:
  1160.                     utterances.append(label.name)
  1161.                 
  1162.                 speech.speakUtterances(utterances)
  1163.                 return None
  1164.         
  1165.         relations = obj.getRelationSet()
  1166.         for relation in relations:
  1167.             if relation.getRelationType() == pyatspi.RELATION_CONTROLLED_BY:
  1168.                 target = relation.getTarget(0)
  1169.                 if target == orca_state.locusOfFocus:
  1170.                     self.updateBraille(target)
  1171.                     utterances = self.speechGenerator.getSpeech(target, True)
  1172.                     utterances.extend(self.tutorialGenerator.getTutorial(target, True))
  1173.                     speech.speakUtterances(utterances)
  1174.                     return None
  1175.                 continue
  1176.             target == orca_state.locusOfFocus
  1177.         
  1178.         if obj.getRole() == pyatspi.ROLE_LABEL and obj.getState().contains(pyatspi.STATE_SHOWING):
  1179.             for relation in relations:
  1180.                 if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR:
  1181.                     target = relation.getTarget(0)
  1182.                     if target == orca_state.locusOfFocus:
  1183.                         self.updateBraille(target)
  1184.                         utterances = self.speechGenerator.getSpeech(target, True)
  1185.                         utterances.extend(self.tutorialGenerator.getTutorial(target, True))
  1186.                         speech.speakUtterances(utterances)
  1187.                         return None
  1188.                     continue
  1189.                 target == orca_state.locusOfFocus
  1190.             
  1191.         
  1192.         if not self.isSameObject(obj, orca_state.locusOfFocus):
  1193.             return None
  1194.         if obj.getRole() == pyatspi.ROLE_RADIO_BUTTON:
  1195.             if orca_state.lastNonModifierKeyEvent and orca_state.lastNonModifierKeyEvent.event_string == 'space':
  1196.                 pass
  1197.             else:
  1198.                 return None
  1199.         orca_state.lastNonModifierKeyEvent.event_string == 'space'
  1200.         if event:
  1201.             debug.println(debug.LEVEL_FINE, "VISUAL CHANGE: '%s' '%s' (event='%s')" % (obj.name, obj.getRole(), event.type))
  1202.         else:
  1203.             debug.println(debug.LEVEL_FINE, "VISUAL CHANGE: '%s' '%s' (event=None)" % (obj.name, obj.getRole()))
  1204.         mag.magnifyAccessible(event, obj)
  1205.         self.updateBraille(obj)
  1206.         utterances = self.speechGenerator.getSpeech(obj, True)
  1207.         utterances.extend(self.tutorialGenerator.getTutorial(obj, True))
  1208.         speech.speakUtterances(utterances)
  1209.  
  1210.     
  1211.     def updateBraille(self, obj, extraRegion = None):
  1212.         '''Updates the braille display to show the give object.
  1213.  
  1214.         Arguments:
  1215.         - obj: the Accessible
  1216.         - extra: extra Region to add to the end
  1217.         '''
  1218.         if not obj:
  1219.             return None
  1220.         braille.clear()
  1221.         line = braille.Line()
  1222.         braille.addLine(line)
  1223.         
  1224.         try:
  1225.             text = obj.queryText()
  1226.         except NotImplementedError:
  1227.             obj
  1228.             obj
  1229.             text = None
  1230.         except:
  1231.             obj
  1232.  
  1233.         if text and self.isTextArea(obj):
  1234.             (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START)
  1235.             if startOffset == 0:
  1236.                 line.addRegions(self.brailleGenerator.getBrailleContext(obj))
  1237.             
  1238.         else:
  1239.             line.addRegions(self.brailleGenerator.getBrailleContext(obj))
  1240.         result = self.brailleGenerator.getBrailleRegions(obj)
  1241.         line.addRegions(result[0])
  1242.         if extraRegion:
  1243.             line.addRegion(extraRegion)
  1244.         
  1245.         if extraRegion:
  1246.             braille.setFocus(extraRegion)
  1247.         else:
  1248.             braille.setFocus(result[1])
  1249.         braille.refresh(True)
  1250.  
  1251.     
  1252.     def onFocus(self, event):
  1253.         '''Called whenever an object gets focus.
  1254.  
  1255.         Arguments:
  1256.         - event: the Event
  1257.         '''
  1258.         role = event.source.getRole()
  1259.         if role in (pyatspi.ROLE_MENU, pyatspi.ROLE_MENU_ITEM, pyatspi.ROLE_CHECK_MENU_ITEM, pyatspi.ROLE_RADIO_MENU_ITEM):
  1260.             
  1261.             try:
  1262.                 if event.source.querySelection().nSelectedChildren > 0:
  1263.                     return None
  1264.  
  1265.         
  1266.         newFocus = event.source
  1267.         if role in (pyatspi.ROLE_LAYERED_PANE, pyatspi.ROLE_TABLE, pyatspi.ROLE_TREE_TABLE, pyatspi.ROLE_TREE):
  1268.             if event.source.childCount:
  1269.                 if 'activeDescendantInfo' in self.pointOfReference:
  1270.                     (parent, index) = self.pointOfReference['activeDescendantInfo']
  1271.                     newFocus = parent[index]
  1272.                 else:
  1273.                     
  1274.                     try:
  1275.                         selection = event.source.querySelection()
  1276.                     except NotImplementedError:
  1277.                         selection = None
  1278.  
  1279.                     if selection and selection.nSelectedChildren > 0:
  1280.                         newFocus = selection.getSelectedChild(0)
  1281.                     
  1282.             
  1283.         
  1284.         orca.setLocusOfFocus(event, newFocus)
  1285.  
  1286.     
  1287.     def onNameChanged(self, event):
  1288.         '''Called whenever a property on an object changes.
  1289.  
  1290.         Arguments:
  1291.         - event: the Event
  1292.         '''
  1293.         if event.source and event.source.getRole() == pyatspi.ROLE_DIALOG and event.source == orca_state.locusOfFocus:
  1294.             return None
  1295.         if self.pointOfReference.get('oldName', None) == event.source.name:
  1296.             return None
  1297.         self.pointOfReference['oldName'] = event.source.name
  1298.         orca.visualAppearanceChanged(event, event.source)
  1299.  
  1300.     
  1301.     def _speakContiguousSelection(self, obj, relationship):
  1302.         '''Check if the contiguous object has a selection. If it does, then
  1303.         speak it. If the user pressed Shift-Down, then look for an object 
  1304.         with a RELATION_FLOWS_FROM relationship. If they pressed Shift-Up,
  1305.         then look for a RELATION_FLOWS_TO relationship.
  1306.  
  1307.         Arguments:
  1308.         - the current text object
  1309.         - the flows relationship (RELATION_FLOWS_FROM or RELATION_FLOWS_TO).
  1310.  
  1311.         Returns an indication of whether anything was spoken.
  1312.         '''
  1313.         lastPos = self.pointOfReference.get('lastCursorPosition')
  1314.         if (self.isSameObject(lastPos[0], obj) or relationship == pyatspi.RELATION_FLOWS_TO) and lastPos[1] == 0:
  1315.             return False
  1316.         selSpoken = False
  1317.         current = obj
  1318.         for relation in current.getRelationSet():
  1319.             if relation.getRelationType() == relationship:
  1320.                 obj = relation.getTarget(0)
  1321.                 objText = obj.queryText()
  1322.                 if relationship == pyatspi.RELATION_FLOWS_FROM:
  1323.                     start = lastPos[1]
  1324.                     end = objText.characterCount
  1325.                 else:
  1326.                     start = 0
  1327.                     end = lastPos[1]
  1328.                 if objText.getNSelections() > 0:
  1329.                     (textContents, startOffset, endOffset) = self.whereAmI.getTextSelection(obj)
  1330.                     if not start:
  1331.                         pass
  1332.                     startOffset = startOffset
  1333.                     if not end:
  1334.                         pass
  1335.                     endOffset = endOffset
  1336.                     self.sayPhrase(obj, startOffset, endOffset)
  1337.                     selSpoken = True
  1338.                 else:
  1339.                     self.sayPhrase(obj, start, end)
  1340.                     selSpoken = True
  1341.             objText.getNSelections() > 0
  1342.         
  1343.         return selSpoken
  1344.  
  1345.     
  1346.     def _presentTextAtNewCaretPosition(self, event, otherObj = None):
  1347.         '''Updates braille, magnification, and outputs speech for the 
  1348.         event.source or the otherObj.'''
  1349.         if not otherObj:
  1350.             pass
  1351.         obj = event.source
  1352.         text = obj.queryText()
  1353.         if obj:
  1354.             mag.magnifyAccessible(event, obj)
  1355.         
  1356.         brailleNeedsRepainting = True
  1357.         line = braille.getShowingLine()
  1358.         for region in line.regions:
  1359.             if isinstance(region, braille.Text) and region.accessible == obj:
  1360.                 if region.repositionCursor():
  1361.                     braille.refresh(True)
  1362.                     brailleNeedsRepainting = False
  1363.                 
  1364.                 break
  1365.                 continue
  1366.         
  1367.         if brailleNeedsRepainting:
  1368.             self.updateBraille(obj)
  1369.         
  1370.         if not orca_state.lastInputEvent:
  1371.             return None
  1372.         if isinstance(orca_state.lastInputEvent, input_event.MouseButtonEvent):
  1373.             if not orca_state.lastInputEvent.pressed:
  1374.                 self.sayLine(obj)
  1375.             
  1376.             return None
  1377.         if not isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent):
  1378.             return None
  1379.         keyString = orca_state.lastNonModifierKeyEvent.event_string
  1380.         mods = orca_state.lastInputEvent.modifiers
  1381.         isControlKey = mods & settings.CTRL_MODIFIER_MASK
  1382.         isShiftKey = mods & settings.SHIFT_MODIFIER_MASK
  1383.         lastPos = self.pointOfReference.get('lastCursorPosition')
  1384.         hasLastPos = lastPos != None
  1385.         if keyString == 'Up' or keyString == 'Down':
  1386.             if hasLastPos and isShiftKey and not isControlKey:
  1387.                 if keyString == 'Up':
  1388.                     self.sayPhrase(obj, startOffset, endOffset)
  1389.                     selSpoken = self._speakContiguousSelection(obj, pyatspi.RELATION_FLOWS_TO)
  1390.                 else:
  1391.                     selSpoken = self._speakContiguousSelection(obj, pyatspi.RELATION_FLOWS_FROM)
  1392.                     if startOffset != endOffset:
  1393.                         self.sayPhrase(obj, startOffset, endOffset)
  1394.                     
  1395.             else:
  1396.                 (startOffset, endOffset) = self.getOffsetsForLine(obj)
  1397.                 self.sayLine(obj)
  1398.         elif keyString == 'Left' or keyString == 'Right':
  1399.             if hasLastPos:
  1400.                 pass
  1401.             inNewObj = not self.isSameObject(lastPos[0], obj)
  1402.             if hasLastPos and not inNewObj and isShiftKey and isControlKey:
  1403.                 (startOffset, endOffset) = self.getOffsetsForPhrase(obj)
  1404.                 self.sayPhrase(obj, startOffset, endOffset)
  1405.             elif isControlKey and not inNewObj:
  1406.                 (startOffset, endOffset) = self.getOffsetsForWord(obj)
  1407.                 if startOffset == endOffset:
  1408.                     self.sayCharacter(obj)
  1409.                 else:
  1410.                     self.sayWord(obj)
  1411.             else:
  1412.                 (startOffset, endOffset) = self.getOffsetsForChar(obj)
  1413.                 self.sayCharacter(obj)
  1414.         elif keyString == 'Page_Up':
  1415.             if hasLastPos and isShiftKey and isControlKey:
  1416.                 (startOffset, endOffset) = self.getOffsetsForPhrase(obj)
  1417.                 self.sayPhrase(obj, startOffset, endOffset)
  1418.             elif isControlKey:
  1419.                 (startOffset, endOffset) = self.getOffsetsForChar(obj)
  1420.                 self.sayCharacter(obj)
  1421.             else:
  1422.                 (startOffset, endOffset) = self.getOffsetsForLine(obj)
  1423.                 self.sayLine(obj)
  1424.         elif keyString == 'Page_Down':
  1425.             if hasLastPos and isShiftKey and isControlKey:
  1426.                 (startOffset, endOffset) = self.getOffsetsForPhrase(obj)
  1427.                 self.sayPhrase(obj, startOffset, endOffset)
  1428.             else:
  1429.                 (startOffset, endOffset) = self.getOffsetsForLine(obj)
  1430.                 self.sayLine(obj)
  1431.         elif keyString == 'Home' or keyString == 'End':
  1432.             if hasLastPos and isShiftKey and not isControlKey:
  1433.                 (startOffset, endOffset) = self.getOffsetsForPhrase(obj)
  1434.                 self.sayPhrase(obj, startOffset, endOffset)
  1435.             elif isControlKey:
  1436.                 (startOffset, endOffset) = self.getOffsetsForLine(obj)
  1437.                 self.sayLine(obj)
  1438.             else:
  1439.                 (startOffset, endOffset) = self.getOffsetsForChar(obj)
  1440.                 self.sayCharacter(obj)
  1441.         else:
  1442.             startOffset = text.caretOffset
  1443.             endOffset = text.caretOffset
  1444.         self._saveLastCursorPosition(obj, text.caretOffset)
  1445.         self._saveSpokenTextRange(startOffset, endOffset)
  1446.  
  1447.     
  1448.     def onCaretMoved(self, event):
  1449.         '''Called whenever the caret moves.
  1450.  
  1451.         Arguments:
  1452.         - event: the Event
  1453.         '''
  1454.         if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus:
  1455.             return None
  1456.         if self.flatReviewContext:
  1457.             self.toggleFlatReviewMode()
  1458.         
  1459.         self._presentTextAtNewCaretPosition(event)
  1460.  
  1461.     
  1462.     def onTextDeleted(self, event):
  1463.         '''Called whenever text is deleted from an object.
  1464.  
  1465.         Arguments:
  1466.         - event: the Event
  1467.         '''
  1468.         if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus:
  1469.             return None
  1470.         if event.source.getRole() == pyatspi.ROLE_SLIDER:
  1471.             return None
  1472.         self.updateBraille(event.source)
  1473.         if not (orca_state.lastInputEvent) or not isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent):
  1474.             return None
  1475.         keyString = orca_state.lastNonModifierKeyEvent.event_string
  1476.         controlPressed = orca_state.lastInputEvent.modifiers & settings.CTRL_MODIFIER_MASK
  1477.         text = event.source.queryText()
  1478.         if keyString == 'BackSpace':
  1479.             character = event.any_data
  1480.         elif (keyString == 'Delete' or keyString == 'D') and controlPressed:
  1481.             offset = text.caretOffset
  1482.             (character, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR)
  1483.         else:
  1484.             return None
  1485.         if (event.source.getRole() == pyatspi.ROLE_SLIDER).getLinkIndex(event.source, text.caretOffset) >= 0:
  1486.             voice = self.voices[settings.HYPERLINK_VOICE]
  1487.         elif character.isupper():
  1488.             voice = self.voices[settings.UPPERCASE_VOICE]
  1489.         else:
  1490.             voice = self.voices[settings.DEFAULT_VOICE]
  1491.         if len(character.decode('utf-8')) == 1:
  1492.             speech.speakCharacter(character, voice)
  1493.         else:
  1494.             speech.speak(character, voice, False)
  1495.  
  1496.     
  1497.     def onTextInserted(self, event):
  1498.         '''Called whenever text is inserted into an object.
  1499.  
  1500.         Arguments:
  1501.         - event: the Event
  1502.         '''
  1503.         if event.source != orca_state.locusOfFocus and event.source.parent != orca_state.locusOfFocus:
  1504.             return None
  1505.         if event.source.getRole() == pyatspi.ROLE_SLIDER:
  1506.             return None
  1507.         self.updateBraille(event.source)
  1508.         text = event.any_data
  1509.         if event.source.getRole() == pyatspi.ROLE_SPIN_BUTTON:
  1510.             (spinValue, caretOffset, startOffset) = self.getTextLineAtCaret(event.source)
  1511.             speech.speak(spinValue)
  1512.             return None
  1513.         speakThis = False
  1514.         if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent):
  1515.             keyString = orca_state.lastNonModifierKeyEvent.event_string
  1516.             if event.source.getRole() == pyatspi.ROLE_TEXT:
  1517.                 pass
  1518.             wasAutoComplete = event.source.queryText().getNSelections()
  1519.             wasCommand = orca_state.lastInputEvent.modifiers & settings.COMMAND_MODIFIER_MASK
  1520.             if text == ' ' or keyString == 'space' or text == keyString:
  1521.                 pass
  1522.             elif wasCommand or wasAutoComplete:
  1523.                 speakThis = True
  1524.             elif event.source.getRole() == pyatspi.ROLE_PASSWORD_TEXT and settings.enableKeyEcho and settings.enablePrintableKeys:
  1525.                 text = '*'
  1526.                 speakThis = True
  1527.             
  1528.         elif isinstance(orca_state.lastInputEvent, input_event.MouseButtonEvent) and orca_state.lastInputEvent.button == '2':
  1529.             speakThis = True
  1530.         
  1531.         if speakThis:
  1532.             if text.isupper():
  1533.                 speech.speak(text, self.voices[settings.UPPERCASE_VOICE])
  1534.             else:
  1535.                 speech.speak(text)
  1536.         
  1537.         
  1538.         try:
  1539.             text = event.source.queryText()
  1540.         except NotImplementedError:
  1541.             return None
  1542.  
  1543.         offset = min(event.detail1, text.caretOffset - 1)
  1544.         previousOffset = offset - 1
  1545.         if offset < 0 or previousOffset < 0:
  1546.             return None
  1547.         (currentChar, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_CHAR)
  1548.         (previousChar, startOffset, endOffset) = text.getTextAtOffset(previousOffset, pyatspi.TEXT_BOUNDARY_CHAR)
  1549.         if settings.enableEchoBySentence and self.isSentenceDelimiter(currentChar, previousChar):
  1550.             self.echoPreviousSentence(event.source)
  1551.         elif settings.enableEchoByWord and self.isWordDelimiter(currentChar):
  1552.             self.echoPreviousWord(event.source)
  1553.         
  1554.  
  1555.     
  1556.     def onActiveDescendantChanged(self, event):
  1557.         '''Called when an object who manages its own descendants detects a
  1558.         change in one of its children.
  1559.  
  1560.         Arguments:
  1561.         - event: the Event
  1562.         '''
  1563.         if not event.source.getState().contains(pyatspi.STATE_FOCUSED):
  1564.             return None
  1565.         child = event.any_data
  1566.         if child:
  1567.             speech.stop()
  1568.             orca.setLocusOfFocus(event, child)
  1569.         else:
  1570.             orca.setLocusOfFocus(event, event.source)
  1571.         if orca_state.locusOfFocus and orca_state.locusOfFocus != event.source:
  1572.             self.pointOfReference['activeDescendantInfo'] = [
  1573.                 orca_state.locusOfFocus.parent,
  1574.                 orca_state.locusOfFocus.getIndexInParent()]
  1575.         
  1576.  
  1577.     
  1578.     def onLinkSelected(self, event):
  1579.         '''Called when a hyperlink is selected in a text area.
  1580.  
  1581.         Arguments:
  1582.         - event: the Event
  1583.         '''
  1584.         orca.setLocusOfFocus(event, event.source)
  1585.  
  1586.     
  1587.     def onStateChanged(self, event):
  1588.         """Called whenever an object's state changes.
  1589.  
  1590.         Arguments:
  1591.         - event: the Event
  1592.         """
  1593.         if event.type.startswith('object:state-changed:active'):
  1594.             if self.findCommandRun:
  1595.                 self.findCommandRun = False
  1596.                 self.find()
  1597.                 return None
  1598.         
  1599.         if event.type.startswith('object:state-changed:selected') and orca_state.locusOfFocus:
  1600.             if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent):
  1601.                 if orca_state.lastNonModifierKeyEvent:
  1602.                     keyString = orca_state.lastNonModifierKeyEvent.event_string
  1603.                 else:
  1604.                     keyString = None
  1605.                 mods = orca_state.lastInputEvent.modifiers
  1606.                 isControlKey = mods & settings.CTRL_MODIFIER_MASK
  1607.                 state = orca_state.locusOfFocus.getState()
  1608.                 announceState = False
  1609.                 if state.contains(pyatspi.STATE_FOCUSED) and self.isSameObject(event.source, orca_state.locusOfFocus):
  1610.                     if keyString == 'space':
  1611.                         if isControlKey:
  1612.                             announceState = True
  1613.                         else:
  1614.                             eventState = event.source.getState()
  1615.                             selected = eventState.contains(pyatspi.STATE_SELECTED)
  1616.                             if selected:
  1617.                                 pass
  1618.                             announceState = event.detail1
  1619.                     
  1620.                     if (keyString == 'Down' or keyString == 'Up') and event.source.getRole() == pyatspi.ROLE_TABLE_CELL and state.contains(pyatspi.STATE_SELECTED):
  1621.                         announceState = True
  1622.                     
  1623.                 
  1624.                 if announceState:
  1625.                     if event.detail1:
  1626.                         speech.speak(C_('text', 'selected'), None, False)
  1627.                     else:
  1628.                         speech.speak(C_('text', 'unselected'), None, False)
  1629.                     return None
  1630.             
  1631.         
  1632.         if event.type.startswith('object:state-changed:focused'):
  1633.             iconified = False
  1634.             
  1635.             try:
  1636.                 window = self.getTopLevel(event.source)
  1637.                 iconified = window.getState().contains(pyatspi.STATE_ICONIFIED)
  1638.             except:
  1639.                 debug.println(debug.LEVEL_FINEST, 'onStateChanged: could not get frame of focused item')
  1640.  
  1641.             if not iconified:
  1642.                 if event.detail1:
  1643.                     self.onFocus(event)
  1644.                 
  1645.                 return None
  1646.         
  1647.         if event.source.getRole() == pyatspi.ROLE_TOOL_TIP:
  1648.             obj = event.source
  1649.             if event.type.startswith('object:state-changed:showing'):
  1650.                 if event.detail1 == 1:
  1651.                     self.presentTooltip(obj)
  1652.                 elif orca_state.locusOfFocus and isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent) and orca_state.lastNonModifierKeyEvent.event_string == 'F1':
  1653.                     self.updateBraille(orca_state.locusOfFocus)
  1654.                     utterances = self.speechGenerator.getSpeech(orca_state.locusOfFocus, False)
  1655.                     utterances.extend(self.tutorialGenerator.getTutorial(orca_state.locusOfFocus, False))
  1656.                     speech.speakUtterances(utterances)
  1657.                 
  1658.             
  1659.             return None
  1660.         if event.source.getRole() in state_change_notifiers:
  1661.             notifiers = state_change_notifiers[event.source.getRole()]
  1662.             found = False
  1663.             for state in notifiers:
  1664.                 if state and event.type.endswith(state):
  1665.                     found = True
  1666.                     break
  1667.                     continue
  1668.                 event.source.getRole() == pyatspi.ROLE_TOOL_TIP
  1669.             
  1670.             if found:
  1671.                 orca.visualAppearanceChanged(event, event.source)
  1672.             
  1673.         
  1674.  
  1675.     
  1676.     def getOffsetsForPhrase(self, obj):
  1677.         '''Return the start and end offset for the given phrase
  1678.  
  1679.         Arguments:
  1680.         - obj: the Accessible object
  1681.         '''
  1682.         text = obj.queryText()
  1683.         lastPos = self.pointOfReference.get('lastCursorPosition')
  1684.         startOffset = lastPos[1]
  1685.         endOffset = text.caretOffset
  1686.         if startOffset > endOffset or endOffset != -1 or startOffset == -1:
  1687.             temp = endOffset
  1688.             endOffset = startOffset
  1689.             startOffset = temp
  1690.         
  1691.         return [
  1692.             startOffset,
  1693.             endOffset]
  1694.  
  1695.     
  1696.     def getOffsetsForLine(self, obj):
  1697.         '''Return the start and end offset for the given line
  1698.  
  1699.         Arguments:
  1700.         - obj: the Accessible object
  1701.         '''
  1702.         (line, endOffset, startOffset) = self.getTextLineAtCaret(obj)
  1703.         return [
  1704.             startOffset,
  1705.             endOffset]
  1706.  
  1707.     
  1708.     def getOffsetsForWord(self, obj):
  1709.         '''Return the start and end offset for the given word
  1710.  
  1711.         Arguments:
  1712.         - obj: the Accessible object
  1713.         '''
  1714.         text = obj.queryText()
  1715.         offset = text.caretOffset
  1716.         (word, startOffset, endOffset) = text.getTextAtOffset(offset, pyatspi.TEXT_BOUNDARY_WORD_START)
  1717.         return [
  1718.             startOffset,
  1719.             endOffset]
  1720.  
  1721.     
  1722.     def getOffsetsForChar(self, obj):
  1723.         '''Return the start and end offset for the given character
  1724.  
  1725.         Arguments:
  1726.         - obj: the Accessible object
  1727.         '''
  1728.         text = obj.queryText()
  1729.         offset = text.caretOffset
  1730.         mods = orca_state.lastInputEvent.modifiers
  1731.         if mods & settings.SHIFT_MODIFIER_MASK and orca_state.lastNonModifierKeyEvent.event_string == 'Right':
  1732.             startOffset = offset - 1
  1733.             endOffset = offset
  1734.         else:
  1735.             startOffset = offset
  1736.             endOffset = offset + 1
  1737.         return [
  1738.             startOffset,
  1739.             endOffset]
  1740.  
  1741.     
  1742.     def onTextSelectionChanged(self, event):
  1743.         """Called when an object's text selection changes.
  1744.  
  1745.         Arguments:
  1746.         - event: the Event
  1747.         """
  1748.         obj = event.source
  1749.         if not self.pointOfReference.get('spokenTextRange'):
  1750.             pass
  1751.         spokenRange = [
  1752.             0,
  1753.             0]
  1754.         (startOffset, endOffset) = spokenRange
  1755.         if not obj.getState().contains(pyatspi.STATE_FOCUSED):
  1756.             lastPos = self.pointOfReference.get('lastCursorPosition')
  1757.             if not lastPos:
  1758.                 return None
  1759.             if endOffset - startOffset > 1:
  1760.                 return None
  1761.             relationType = None
  1762.             for relation in lastPos[0].getRelationSet():
  1763.                 if relation.getRelationType() in [
  1764.                     pyatspi.RELATION_FLOWS_FROM,
  1765.                     pyatspi.RELATION_FLOWS_TO] and self.isSameObject(obj, relation.getTarget(0)):
  1766.                     relationType = relation.getRelationType()
  1767.                     break
  1768.                     continue
  1769.                 endOffset - startOffset > 1
  1770.             
  1771.             endOffset = 0
  1772.             while obj and not endOffset:
  1773.                 
  1774.                 try:
  1775.                     endOffset = obj.queryText().characterCount
  1776.                     startOffset = max(0, endOffset - 1)
  1777.                 except:
  1778.                     lastPos
  1779.  
  1780.                 if not endOffset:
  1781.                     for relation in obj.getRelationSet():
  1782.                         if relation.getRelationType() == relationType:
  1783.                             obj = relation.getTarget(0)
  1784.                             break
  1785.                             continue
  1786.                         lastPos
  1787.                     else:
  1788.                         break
  1789.         
  1790.         self.speakTextSelectionState(obj, startOffset, endOffset)
  1791.  
  1792.     
  1793.     def onSelectionChanged(self, event):
  1794.         """Called when an object's selection changes.
  1795.  
  1796.         Arguments:
  1797.         - event: the Event
  1798.         """
  1799.         if not event or not (event.source):
  1800.             return None
  1801.         if event.source.getRole() in (pyatspi.ROLE_COMBO_BOX, pyatspi.ROLE_MENU):
  1802.             self.lastSelectedMenu = event.source
  1803.         
  1804.         if event.source.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS):
  1805.             return None
  1806.         if event.source.getRole() == pyatspi.ROLE_COMBO_BOX:
  1807.             orca.visualAppearanceChanged(event, event.source)
  1808.         elif event.source != orca_state.locusOfFocus and event.source.getState().contains(pyatspi.STATE_FOCUSED):
  1809.             newFocus = event.source
  1810.             if event.source.childCount:
  1811.                 selection = event.source.querySelection()
  1812.                 if selection.nSelectedChildren > 0:
  1813.                     newFocus = selection.getSelectedChild(0)
  1814.                 
  1815.             
  1816.             orca.setLocusOfFocus(event, newFocus)
  1817.         
  1818.  
  1819.     
  1820.     def onValueChanged(self, event):
  1821.         """Called whenever an object's value changes.  Currently, the
  1822.         value changes for non-focused objects are ignored.
  1823.  
  1824.         Arguments:
  1825.         - event: the Event
  1826.         """
  1827.         if event.source.getRole() == pyatspi.ROLE_SPIN_BUTTON:
  1828.             return None
  1829.         value = event.source.queryValue()
  1830.         if 'oldValue' in self.pointOfReference and value.currentValue == self.pointOfReference['oldValue']:
  1831.             return None
  1832.         orca.visualAppearanceChanged(event, event.source)
  1833.  
  1834.     
  1835.     def onWindowActivated(self, event):
  1836.         '''Called whenever a toplevel window is activated.
  1837.  
  1838.         Arguments:
  1839.         - event: the Event
  1840.         '''
  1841.         self.windowActivateTime = time.time()
  1842.         orca.setLocusOfFocus(event, event.source)
  1843.         orca_state.activeWindow = event.source
  1844.  
  1845.     
  1846.     def onWindowDeactivated(self, event):
  1847.         '''Called whenever a toplevel window is deactivated.
  1848.  
  1849.         Arguments:
  1850.         - event: the Event
  1851.         '''
  1852.         if orca_state.locusOfFocus and orca_state.locusOfFocus.getApplication() == event.source.getApplication():
  1853.             speech.stop()
  1854.             braille.clear()
  1855.             if self.flatReviewContext:
  1856.                 self.drawOutline(-1, 0, 0, 0)
  1857.                 self.flatReviewContext = None
  1858.                 self.updateBraille(orca_state.locusOfFocus)
  1859.             
  1860.         
  1861.         if event.source == orca_state.activeWindow:
  1862.             orca.setLocusOfFocus(event, None)
  1863.             orca_state.activeWindow = None
  1864.         
  1865.  
  1866.     
  1867.     def onMouseButton(self, event):
  1868.         '''Called whenever the user presses or releases a mouse button.
  1869.  
  1870.         Arguments:
  1871.         - event: the Event
  1872.         '''
  1873.         state = event.type[-1]
  1874.         if state == 'r':
  1875.             obj = orca_state.locusOfFocus
  1876.             
  1877.             try:
  1878.                 text = obj.queryText()
  1879.             except:
  1880.                 pass
  1881.  
  1882.             (textContents, startOffset, endOffset) = self.whereAmI.getTextSelections(obj, True)
  1883.             if textContents:
  1884.                 utterances = []
  1885.                 utterances.append(textContents)
  1886.                 utterances.append(C_('text', 'selected'))
  1887.                 speech.speakUtterances(utterances)
  1888.             
  1889.             self.updateBraille(orca_state.locusOfFocus)
  1890.         
  1891.  
  1892.     
  1893.     def noOp(self, event):
  1894.         '''Just here to capture events.
  1895.  
  1896.         Arguments:
  1897.         - event: the Event
  1898.         '''
  1899.         pass
  1900.  
  1901.     
  1902.     def isLayoutOnly(self, obj):
  1903.         '''Returns True if the given object is a table and is for layout
  1904.         purposes only.'''
  1905.         layoutOnly = False
  1906.         if obj:
  1907.             attributes = obj.getAttributes()
  1908.         else:
  1909.             attributes = None
  1910.         if obj and obj.getRole() == pyatspi.ROLE_TABLE and attributes:
  1911.             for attribute in attributes:
  1912.                 if attribute == 'layout-guess:true':
  1913.                     layoutOnly = True
  1914.                     break
  1915.                     continue
  1916.             
  1917.         elif obj and obj.getRole() == pyatspi.ROLE_PANEL:
  1918.             text = self.getDisplayedText(obj)
  1919.             label = self.getDisplayedLabel(obj)
  1920.             if not (label or len(label)) and text and len(text):
  1921.                 layoutOnly = True
  1922.             
  1923.         
  1924.         if layoutOnly:
  1925.             debug.println(debug.LEVEL_FINEST, 'Object deemed to be for layout purposes only: %s' % obj)
  1926.         
  1927.         return layoutOnly
  1928.  
  1929.     
  1930.     def toggleTableCellReadMode(self, inputEvent = None):
  1931.         '''Toggles an indicator for whether we should just read the current
  1932.         table cell or read the whole row.'''
  1933.         settings.readTableCellRow = not (settings.readTableCellRow)
  1934.         if settings.readTableCellRow:
  1935.             line = _('Speak row')
  1936.         else:
  1937.             line = _('Speak cell')
  1938.         speech.speak(line)
  1939.         return True
  1940.  
  1941.     
  1942.     def getAtkNameForAttribute(self, attribName):
  1943.         '''Converts the given attribute name into the Atk equivalent. This
  1944.         is necessary because an application or toolkit (e.g. Gecko) might
  1945.         invent entirely new names for the same attributes.
  1946.  
  1947.         Arguments:
  1948.         - attribName: The name of the text attribute
  1949.  
  1950.         Returns the Atk equivalent name if found or attribName otherwise.
  1951.         '''
  1952.         return self.attributeNamesDict.get(attribName, attribName)
  1953.  
  1954.     
  1955.     def getAppNameForAttribute(self, attribName):
  1956.         """Converts the given Atk attribute name into the application's
  1957.         equivalent. This is necessary because an application or toolkit
  1958.         (e.g. Gecko) might invent entirely new names for the same text
  1959.         attributes.
  1960.  
  1961.         Arguments:
  1962.         - attribName: The name of the text attribute
  1963.  
  1964.         Returns the application's equivalent name if found or attribName
  1965.         otherwise.
  1966.         """
  1967.         for key, value in self.attributeNamesDict.items():
  1968.             if value == attribName:
  1969.                 return key
  1970.         
  1971.         return attribName
  1972.  
  1973.     
  1974.     def textAttrsToDictionary(self, tokenString):
  1975.         """Converts a string of text attribute tokens of the form
  1976.         <key>:<value>; into a dictionary of keys and values.
  1977.         Text before the colon is the key and text afterwards is the
  1978.         value. If there is a final semi-colon, then it's ignored.
  1979.  
  1980.         Arguments:
  1981.         - tokenString: the string of tokens containing <key>:<value>; pairs.
  1982.  
  1983.         Returns a list containing two items:
  1984.         A list of the keys in the order they were extracted from the
  1985.         text attribute string and a dictionary of key/value items.
  1986.         """
  1987.         keyList = []
  1988.         dictionary = { }
  1989.         allTokens = tokenString.split(';')
  1990.         for token in allTokens:
  1991.             item = token.split(':')
  1992.             if len(item) == 2:
  1993.                 key = item[0].strip()
  1994.                 attribute = item[1].strip()
  1995.                 keyList.append(key)
  1996.                 dictionary[key] = attribute
  1997.                 continue
  1998.         
  1999.         return [
  2000.             keyList,
  2001.             dictionary]
  2002.  
  2003.     
  2004.     def outputCharAttributes(self, keys, attributes):
  2005.         '''Speak each of the text attributes given dictionary.
  2006.  
  2007.         Arguments:
  2008.         - attributes: a dictionary of text attributes to speak.
  2009.         '''
  2010.         for key in keys:
  2011.             localizedKey = text_attribute_names.getTextAttributeName(key)
  2012.             if key in attributes:
  2013.                 line = ''
  2014.                 attribute = attributes[key]
  2015.                 localizedValue = text_attribute_names.getTextAttributeName(attribute)
  2016.                 if attribute:
  2017.                     key = self.getAtkNameForAttribute(key)
  2018.                     if key == 'weight':
  2019.                         if attribute == 'bold' or int(attribute) > 400:
  2020.                             line = _('bold')
  2021.                         elif key in ('left-margin', 'right-margin'):
  2022.                             numericPoint = locale.localeconv()['decimal_point']
  2023.                             lastChar = attribute[len(attribute) - 1]
  2024.                             if lastChar == numericPoint or lastChar in self.digits:
  2025.                                 line = ngettext('%s %s pixel', '%s %s pixels', int(attribute)) % (localizedKey, localizedValue)
  2026.                             
  2027.                         elif key in ('indent', 'size'):
  2028.                             value = attribute.split('px')
  2029.                             if len(value) > 1:
  2030.                                 line = ngettext('%s %s pixel', '%s %s pixels', float(value[0])) % (localizedKey, value[0])
  2031.                             
  2032.                         elif key == 'family-name':
  2033.                             localizedValue = attribute.split(',')[0].strip().strip('"')
  2034.                         
  2035.                     if not line:
  2036.                         pass
  2037.                     line = localizedKey + ' ' + localizedValue
  2038.                     speech.speak(line)
  2039.                 
  2040.             attribute
  2041.         
  2042.  
  2043.     
  2044.     def readCharAttributes(self, inputEvent = None):
  2045.         '''Reads the attributes associated with the current text character.
  2046.         Calls outCharAttributes to speak a list of attributes. By default,
  2047.         a certain set of attributes will be spoken. If this is not desired,
  2048.         then individual application scripts should override this method to
  2049.         only speak the subset required.
  2050.         '''
  2051.         
  2052.         try:
  2053.             text = orca_state.locusOfFocus.queryText()
  2054.         except:
  2055.             pass
  2056.  
  2057.         caretOffset = text.caretOffset
  2058.         defAttributes = text.getDefaultAttributes()
  2059.         debug.println(debug.LEVEL_FINEST, 'readCharAttributes: default text attributes: %s' % defAttributes)
  2060.         (defUser, defDict) = self.textAttrsToDictionary(defAttributes)
  2061.         allAttributes = defDict
  2062.         charAttributes = text.getAttributes(caretOffset)
  2063.         if charAttributes[0]:
  2064.             (charList, charDict) = self.textAttrsToDictionary(charAttributes[0])
  2065.             if allAttributes:
  2066.                 for key in charDict.keys():
  2067.                     allAttributes[key] = charDict[key]
  2068.                 
  2069.             else:
  2070.                 allAttributes = charDict
  2071.         
  2072.         (userAttrList, userAttrDict) = self.textAttrsToDictionary(settings.enabledSpokenTextAttributes)
  2073.         attributes = { }
  2074.         for key in userAttrList:
  2075.             if key in allAttributes:
  2076.                 textAttr = allAttributes.get(key)
  2077.                 userAttr = userAttrDict.get(key)
  2078.                 if textAttr != userAttr or len(userAttr) == 0:
  2079.                     attributes[key] = textAttr
  2080.                 
  2081.             len(userAttr) == 0
  2082.         
  2083.         self.outputCharAttributes(userAttrList, attributes)
  2084.         if self.getLinkIndex(orca_state.locusOfFocus, caretOffset) >= 0:
  2085.             speech.speak(_('link'))
  2086.         
  2087.         return True
  2088.  
  2089.     
  2090.     def reportScriptInfo(self, inputEvent = None):
  2091.         '''Output useful information on the current script via speech
  2092.         and braille.  This information will be helpful to script writers.
  2093.         '''
  2094.         infoString = "SCRIPT INFO: Script name='%s'" % self.name
  2095.         app = orca_state.locusOfFocus.getApplication()
  2096.         if orca_state.locusOfFocus and app:
  2097.             infoString += " Application name='%s'" % app.name
  2098.             
  2099.             try:
  2100.                 infoString += " Toolkit name='%s'" % app.toolkitName
  2101.             except:
  2102.                 infoString += ' Toolkit unknown'
  2103.  
  2104.             
  2105.             try:
  2106.                 infoString += " Version='%s'" % app.version
  2107.             except:
  2108.                 infoString += ' Version unknown'
  2109.  
  2110.             debug.println(debug.LEVEL_INFO, infoString)
  2111.             speech.speak(infoString)
  2112.             braille.displayMessage(infoString)
  2113.         
  2114.         return True
  2115.  
  2116.     
  2117.     def bypassNextCommand(self, inputEvent = None):
  2118.         '''Causes the next keyboard command to be ignored by Orca
  2119.         and passed along to the current application.
  2120.  
  2121.         Returns True to indicate the input event has been consumed.
  2122.         '''
  2123.         speech.speak(_('Bypass mode enabled.'))
  2124.         orca_state.bypassNextCommand = True
  2125.         return True
  2126.  
  2127.     
  2128.     def enterLearnMode(self, inputEvent = None):
  2129.         '''Turns learn mode on.  The user must press the escape key to exit
  2130.         learn mode.
  2131.  
  2132.         Returns True to indicate the input event has been consumed.
  2133.         '''
  2134.         if settings.learnModeEnabled:
  2135.             return True
  2136.         speech.speak(_('Entering learn mode.  Press any key to hear its function.  To exit learn mode, press the escape key.'))
  2137.         braille.displayMessage(_('Learn mode.  Press escape to exit.'))
  2138.         settings.learnModeEnabled = True
  2139.         return True
  2140.  
  2141.     
  2142.     def pursueForFlatReview(self, obj):
  2143.         '''Determines if we should look any further at the object
  2144.         for flat review.'''
  2145.         
  2146.         try:
  2147.             state = obj.getState()
  2148.         except:
  2149.             debug.printException(debug.LEVEL_WARNING)
  2150.             return False
  2151.  
  2152.         return state.contains(pyatspi.STATE_SHOWING)
  2153.  
  2154.     
  2155.     def getShowingDescendants(self, parent):
  2156.         '''Given a parent that manages its descendants, return a list of
  2157.         Accessible children that are actually showing.  This algorithm
  2158.         was inspired a little by the srw_elements_from_accessible logic
  2159.         in Gnopernicus, and makes the assumption that the children of
  2160.         an object that manages its descendants are arranged in a row
  2161.         and column format.
  2162.  
  2163.         Arguments:
  2164.         - parent: The accessible which manages its descendants
  2165.  
  2166.         Returns a list of Accessible descendants which are showing.
  2167.         '''
  2168.         if not parent:
  2169.             return []
  2170.         if not parent.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) or parent.childCount <= 50:
  2171.             return []
  2172.         
  2173.         try:
  2174.             icomponent = parent.queryComponent()
  2175.         except NotImplementedError:
  2176.             parent.childCount <= 50
  2177.             parent.childCount <= 50
  2178.             parent
  2179.             return []
  2180.  
  2181.         descendants = []
  2182.         parentExtents = icomponent.getExtents(0)
  2183.         
  2184.         try:
  2185.             table = parent.queryTable()
  2186.         except NotImplementedError:
  2187.             parent.childCount <= 50
  2188.             parent.childCount <= 50
  2189.             parent
  2190.             table = None
  2191.         except:
  2192.             parent.childCount <= 50
  2193.  
  2194.         gridSize = 7
  2195.         currentY = parentExtents.y
  2196.         while currentY < parentExtents.y + parentExtents.height:
  2197.             currentX = parentExtents.x
  2198.             minHeight = sys.maxint
  2199.             while currentX < parentExtents.x + parentExtents.width:
  2200.                 child = icomponent.getAccessibleAtPoint(currentX, currentY + 1, 0)
  2201.                 if child:
  2202.                     extents = child.queryComponent().getExtents(0)
  2203.                     if extents.x >= 0 and extents.y >= 0:
  2204.                         newX = extents.x + extents.width
  2205.                         minHeight = min(minHeight, extents.height)
  2206.                         if not descendants.count(child):
  2207.                             descendants.append(child)
  2208.                         
  2209.                     else:
  2210.                         newX = currentX + gridSize
  2211.                 else:
  2212.                     newX = currentX + gridSize
  2213.                 if newX <= currentX:
  2214.                     currentX += gridSize
  2215.                     continue
  2216.                 currentX = newX
  2217.             if minHeight == sys.maxint:
  2218.                 minHeight = gridSize
  2219.             
  2220.             currentY += minHeight
  2221.         return descendants
  2222.  
  2223.     
  2224.     def visible(self, ax, ay, awidth, aheight, bx, by, bwidth, bheight):
  2225.         """Returns true if any portion of region 'a' is in region 'b'
  2226.         """
  2227.         highestBottom = min(ay + aheight, by + bheight)
  2228.         lowestTop = max(ay, by)
  2229.         leftMostRightEdge = min(ax + awidth, bx + bwidth)
  2230.         rightMostLeftEdge = max(ax, bx)
  2231.         visible = False
  2232.         if lowestTop <= highestBottom and rightMostLeftEdge <= leftMostRightEdge:
  2233.             visible = True
  2234.         elif aheight == 0:
  2235.             if awidth == 0:
  2236.                 if lowestTop == highestBottom:
  2237.                     pass
  2238.                 visible = leftMostRightEdge == rightMostLeftEdge
  2239.             else:
  2240.                 visible = leftMostRightEdge <= rightMostLeftEdge
  2241.         elif awidth == 0:
  2242.             visible = lowestTop <= highestBottom
  2243.         
  2244.         return visible
  2245.  
  2246.     
  2247.     def getFlatReviewContext(self):
  2248.         '''Returns the flat review context, creating one if necessary.'''
  2249.         if not self.flatReviewContext:
  2250.             self.flatReviewContext = self.flatReviewContextClass(self)
  2251.             self.justEnteredFlatReviewMode = True
  2252.             self.targetCursorCell = braille.cursorCell
  2253.         
  2254.         return self.flatReviewContext
  2255.  
  2256.     
  2257.     def toggleFlatReviewMode(self, inputEvent = None):
  2258.         '''Toggles between flat review mode and focus tracking mode.'''
  2259.         if self.flatReviewContext:
  2260.             self.drawOutline(-1, 0, 0, 0)
  2261.             self.flatReviewContext = None
  2262.             self.updateBraille(orca_state.locusOfFocus)
  2263.         else:
  2264.             context = self.getFlatReviewContext()
  2265.             (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.WORD)
  2266.             self.drawOutline(x, y, width, height)
  2267.             self._reviewCurrentItem(inputEvent, self.targetCursorCell)
  2268.         return True
  2269.  
  2270.     
  2271.     def updateBrailleReview(self, targetCursorCell = 0):
  2272.         '''Obtains the braille regions for the current flat review line
  2273.         and displays them on the braille display.  If the targetCursorCell
  2274.         is non-0, then an attempt will be made to postion the review cursor
  2275.         at that cell.  Otherwise, we will pan in display-sized increments
  2276.         to show the review cursor.'''
  2277.         context = self.getFlatReviewContext()
  2278.         (regions, regionWithFocus) = context.getCurrentBrailleRegions()
  2279.         if not regions:
  2280.             regions = []
  2281.             regionWithFocus = None
  2282.         
  2283.         line = braille.Line()
  2284.         line.addRegions(regions)
  2285.         braille.setLines([
  2286.             line])
  2287.         braille.setFocus(regionWithFocus, False)
  2288.         if regionWithFocus:
  2289.             braille.panToOffset(regionWithFocus.brailleOffset + regionWithFocus.cursorOffset)
  2290.         
  2291.         if self.justEnteredFlatReviewMode:
  2292.             braille.refresh(True, self.targetCursorCell)
  2293.             self.justEnteredFlatReviewMode = False
  2294.         else:
  2295.             braille.refresh(True, targetCursorCell)
  2296.  
  2297.     
  2298.     def _setFlatReviewContextToBeginningOfBrailleDisplay(self):
  2299.         '''Sets the character of interest to be the first character showing
  2300.         at the beginning of the braille display.'''
  2301.         context = self.getFlatReviewContext()
  2302.         (regions, regionWithFocus) = context.getCurrentBrailleRegions()
  2303.         for region in regions:
  2304.             if region.brailleOffset + len(region.string.decode('UTF-8')) > braille.viewport[0]:
  2305.                 if isinstance(region, braille.ReviewText) or isinstance(region, braille.ReviewComponent):
  2306.                     position = max(region.brailleOffset, braille.viewport[0])
  2307.                     offset = position - region.brailleOffset
  2308.                     self.targetCursorCell = region.brailleOffset - braille.viewport[0]
  2309.                     (word, charOffset) = region.zone.getWordAtOffset(offset)
  2310.                     if word:
  2311.                         self.flatReviewContext.setCurrent(word.zone.line.index, word.zone.index, word.index, charOffset)
  2312.                     else:
  2313.                         self.flatReviewContext.setCurrent(region.zone.line.index, region.zone.index, 0, 0)
  2314.                     break
  2315.                     continue
  2316.         
  2317.  
  2318.     
  2319.     def panBrailleLeft(self, inputEvent = None, panAmount = 0):
  2320.         '''Pans the braille display to the left.  If panAmount is non-zero,
  2321.         the display is panned by that many cells.  If it is 0, the display
  2322.         is panned one full display width.  In flat review mode, panning
  2323.         beyond the beginning will take you to the end of the previous line.
  2324.  
  2325.         In focus tracking mode, the cursor stays at its logical position.
  2326.         In flat review mode, the review cursor moves to character
  2327.         associated with cell 0.'''
  2328.         if self.flatReviewContext:
  2329.             if braille.beginningIsShowing:
  2330.                 self.flatReviewContext.goBegin(flat_review.Context.LINE)
  2331.                 self.reviewPreviousCharacter(inputEvent)
  2332.             else:
  2333.                 braille.panLeft(panAmount)
  2334.             self._setFlatReviewContextToBeginningOfBrailleDisplay()
  2335.             (charString, x, y, width, height) = self.flatReviewContext.getCurrent(flat_review.Context.CHAR)
  2336.             self.drawOutline(x, y, width, height)
  2337.             self.targetCursorCell = 1
  2338.             self.updateBrailleReview(self.targetCursorCell)
  2339.         elif braille.beginningIsShowing and orca_state.locusOfFocus and self.isTextArea(orca_state.locusOfFocus):
  2340.             text = orca_state.locusOfFocus.queryText()
  2341.             (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START)
  2342.             movedCaret = False
  2343.             if startOffset > 0:
  2344.                 movedCaret = text.setCaretOffset(startOffset - 1)
  2345.             
  2346.             if not movedCaret and orca_state.locusOfFocus.getRole() == pyatspi.ROLE_TERMINAL:
  2347.                 context = self.getFlatReviewContext()
  2348.                 context.goBegin(flat_review.Context.LINE)
  2349.                 self.reviewPreviousCharacter(inputEvent)
  2350.             
  2351.         else:
  2352.             braille.panLeft(panAmount)
  2353.             braille.refresh(False)
  2354.         return True
  2355.  
  2356.     
  2357.     def panBrailleLeftOneChar(self, inputEvent = None):
  2358.         '''Nudges the braille display one character to the left.
  2359.  
  2360.         In focus tracking mode, the cursor stays at its logical position.
  2361.         In flat review mode, the review cursor moves to character
  2362.         associated with cell 0.'''
  2363.         self.panBrailleLeft(inputEvent, 1)
  2364.  
  2365.     
  2366.     def panBrailleRight(self, inputEvent = None, panAmount = 0):
  2367.         '''Pans the braille display to the right.  If panAmount is non-zero,
  2368.         the display is panned by that many cells.  If it is 0, the display
  2369.         is panned one full display width.  In flat review mode, panning
  2370.         beyond the end will take you to the begininng of the next line.
  2371.  
  2372.         In focus tracking mode, the cursor stays at its logical position.
  2373.         In flat review mode, the review cursor moves to character
  2374.         associated with cell 0.'''
  2375.         if self.flatReviewContext:
  2376.             if braille.endIsShowing:
  2377.                 self.flatReviewContext.goEnd(flat_review.Context.LINE)
  2378.                 self.reviewNextCharacter(inputEvent)
  2379.             else:
  2380.                 braille.panRight(panAmount)
  2381.             self._setFlatReviewContextToBeginningOfBrailleDisplay()
  2382.             (charString, x, y, width, height) = self.flatReviewContext.getCurrent(flat_review.Context.CHAR)
  2383.             self.drawOutline(x, y, width, height)
  2384.             self.targetCursorCell = 1
  2385.             self.updateBrailleReview(self.targetCursorCell)
  2386.         elif braille.endIsShowing and orca_state.locusOfFocus and self.isTextArea(orca_state.locusOfFocus):
  2387.             text = orca_state.locusOfFocus.queryText()
  2388.             (lineString, startOffset, endOffset) = text.getTextAtOffset(text.caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START)
  2389.             if endOffset < text.characterCount:
  2390.                 text.setCaretOffset(endOffset)
  2391.             
  2392.         else:
  2393.             braille.panRight(panAmount)
  2394.             braille.refresh(False)
  2395.         return True
  2396.  
  2397.     
  2398.     def panBrailleRightOneChar(self, inputEvent = None):
  2399.         '''Nudges the braille display one character to the right.
  2400.  
  2401.         In focus tracking mode, the cursor stays at its logical position.
  2402.         In flat review mode, the review cursor moves to character
  2403.         associated with cell 0.'''
  2404.         self.panBrailleRight(inputEvent, 1)
  2405.  
  2406.     
  2407.     def goBrailleHome(self, inputEvent = None):
  2408.         '''Returns to the component with focus.'''
  2409.         if self.flatReviewContext:
  2410.             return self.toggleFlatReviewMode(inputEvent)
  2411.         return braille.returnToRegionWithFocus(inputEvent)
  2412.  
  2413.     
  2414.     def leftClickReviewItem(self, inputEvent = None):
  2415.         '''Performs a left mouse button click on the current item.'''
  2416.         self.getFlatReviewContext().clickCurrent(1)
  2417.         return True
  2418.  
  2419.     
  2420.     def rightClickReviewItem(self, inputEvent = None):
  2421.         '''Performs a right mouse button click on the current item.'''
  2422.         self.getFlatReviewContext().clickCurrent(3)
  2423.         return True
  2424.  
  2425.     
  2426.     def reviewCurrentLine(self, inputEvent):
  2427.         '''Brailles and speaks the current flat review line.'''
  2428.         self._reviewCurrentLine(inputEvent, 1)
  2429.         self.lastReviewCurrentEvent = inputEvent
  2430.         return True
  2431.  
  2432.     
  2433.     def reviewSpellCurrentLine(self, inputEvent):
  2434.         '''Brailles and spells the current flat review line.'''
  2435.         self._reviewCurrentLine(inputEvent, 2)
  2436.         self.lastReviewCurrentEvent = inputEvent
  2437.         return True
  2438.  
  2439.     
  2440.     def reviewPhoneticCurrentLine(self, inputEvent):
  2441.         '''Brailles and phonetically spells the current flat review line.'''
  2442.         self._reviewCurrentLine(inputEvent, 3)
  2443.         self.lastReviewCurrentEvent = inputEvent
  2444.         return True
  2445.  
  2446.     
  2447.     def _reviewCurrentLine(self, inputEvent, speechType = 1):
  2448.         '''Presents the current flat review line via braille and speech.
  2449.  
  2450.         Arguments:
  2451.         - inputEvent - the current input event.
  2452.         - speechType - the desired presentation: speak (1), spell (2), or
  2453.                        phonetic (3)
  2454.         '''
  2455.         context = self.getFlatReviewContext()
  2456.         (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE)
  2457.         self.drawOutline(x, y, width, height)
  2458.         if not isinstance(inputEvent, input_event.BrailleEvent):
  2459.             if not lineString and not len(lineString) or lineString == '\n':
  2460.                 speech.speak(_('blank'))
  2461.             elif lineString.isspace():
  2462.                 speech.speak(_('white space'))
  2463.             elif lineString.isupper():
  2464.                 pass
  2465.             None if speechType < 2 or speechType > 3 else speechType > 3
  2466.             if speechType == 2:
  2467.                 self.spellCurrentItem(lineString)
  2468.             elif speechType == 3:
  2469.                 self.phoneticSpellCurrentItem(lineString)
  2470.             else:
  2471.                 lineString = self.adjustForRepeats(lineString)
  2472.                 speech.speak(lineString)
  2473.         
  2474.         self.updateBrailleReview()
  2475.         return True
  2476.  
  2477.     
  2478.     def reviewPreviousLine(self, inputEvent):
  2479.         '''Moves the flat review context to the beginning of the
  2480.         previous line.'''
  2481.         context = self.getFlatReviewContext()
  2482.         moved = context.goPrevious(flat_review.Context.LINE, flat_review.Context.WRAP_LINE)
  2483.         if moved:
  2484.             self._reviewCurrentLine(inputEvent)
  2485.             self.targetCursorCell = braille.cursorCell
  2486.         
  2487.         return True
  2488.  
  2489.     
  2490.     def reviewHome(self, inputEvent):
  2491.         '''Moves the flat review context to the top left of the current
  2492.         window.'''
  2493.         context = self.getFlatReviewContext()
  2494.         context.goBegin()
  2495.         self._reviewCurrentLine(inputEvent)
  2496.         self.targetCursorCell = braille.cursorCell
  2497.         return True
  2498.  
  2499.     
  2500.     def reviewNextLine(self, inputEvent):
  2501.         '''Moves the flat review context to the beginning of the
  2502.         next line.  Places the flat review cursor at the beginning
  2503.         of the line.'''
  2504.         context = self.getFlatReviewContext()
  2505.         moved = context.goNext(flat_review.Context.LINE, flat_review.Context.WRAP_LINE)
  2506.         if moved:
  2507.             self._reviewCurrentLine(inputEvent)
  2508.             self.targetCursorCell = braille.cursorCell
  2509.         
  2510.         return True
  2511.  
  2512.     
  2513.     def reviewBottomLeft(self, inputEvent):
  2514.         '''Moves the flat review context to the beginning of the
  2515.         last line in the window.  Places the flat review cursor at
  2516.         the beginning of the line.'''
  2517.         context = self.getFlatReviewContext()
  2518.         context.goEnd(flat_review.Context.WINDOW)
  2519.         context.goBegin(flat_review.Context.LINE)
  2520.         self._reviewCurrentLine(inputEvent)
  2521.         self.targetCursorCell = braille.cursorCell
  2522.         return True
  2523.  
  2524.     
  2525.     def reviewEnd(self, inputEvent):
  2526.         '''Moves the flat review context to the end of the
  2527.         last line in the window.  Places the flat review cursor
  2528.         at the end of the line.'''
  2529.         context = self.getFlatReviewContext()
  2530.         context.goEnd()
  2531.         self._reviewCurrentLine(inputEvent)
  2532.         self.targetCursorCell = braille.cursorCell
  2533.         return True
  2534.  
  2535.     
  2536.     def reviewCurrentItem(self, inputEvent, targetCursorCell = 0):
  2537.         '''Brailles and speaks the current item to the user.'''
  2538.         self._reviewCurrentItem(inputEvent, targetCursorCell, 1)
  2539.         self.lastReviewCurrentEvent = inputEvent
  2540.         return True
  2541.  
  2542.     
  2543.     def reviewSpellCurrentItem(self, inputEvent, targetCursorCell = 0):
  2544.         '''Brailles and spells the current item to the user.'''
  2545.         self._reviewCurrentItem(inputEvent, targetCursorCell, 2)
  2546.         self.lastReviewCurrentEvent = inputEvent
  2547.         return True
  2548.  
  2549.     
  2550.     def reviewPhoneticCurrentItem(self, inputEvent, targetCursorCell = 0):
  2551.         '''Brailles and phonetically spells the current item to the user.'''
  2552.         self._reviewCurrentItem(inputEvent, targetCursorCell, 3)
  2553.         self.lastReviewCurrentEvent = inputEvent
  2554.         return True
  2555.  
  2556.     
  2557.     def spellCurrentItem(self, itemString):
  2558.         '''Spell the current flat review word or line.
  2559.  
  2560.         Arguments:
  2561.         - itemString: the string to spell.
  2562.         '''
  2563.         for charIndex, character in enumerate(itemString.decode('UTF-8')):
  2564.             if character.isupper():
  2565.                 speech.speak(character.encode('UTF-8'), self.voices[settings.UPPERCASE_VOICE])
  2566.                 continue
  2567.             speech.speak(character.encode('UTF-8'))
  2568.         
  2569.  
  2570.     
  2571.     def _reviewCurrentItem(self, inputEvent, targetCursorCell = 0, speechType = 1):
  2572.         '''Presents the current item to the user.
  2573.  
  2574.         Arguments:
  2575.         - inputEvent - the current input event.
  2576.         - targetCursorCell - if non-zero, the target braille cursor cell.
  2577.         - speechType - the desired presentation: speak (1), spell (2), or
  2578.                        phonetic (3).
  2579.         '''
  2580.         context = self.getFlatReviewContext()
  2581.         (wordString, x, y, width, height) = context.getCurrent(flat_review.Context.WORD)
  2582.         self.drawOutline(x, y, width, height)
  2583.         if not isinstance(inputEvent, input_event.BrailleEvent):
  2584.             if not wordString and not len(wordString) or wordString == '\n':
  2585.                 speech.speak(_('blank'))
  2586.             else:
  2587.                 (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE)
  2588.                 if lineString == '\n':
  2589.                     speech.speak(_('blank'))
  2590.                 elif wordString.isspace():
  2591.                     speech.speak(_('white space'))
  2592.                 elif wordString.isupper() and speechType == 1:
  2593.                     speech.speak(wordString, self.voices[settings.UPPERCASE_VOICE])
  2594.                 elif speechType == 2:
  2595.                     self.spellCurrentItem(wordString)
  2596.                 elif speechType == 3:
  2597.                     self.phoneticSpellCurrentItem(wordString)
  2598.                 elif speechType == 1:
  2599.                     wordString = self.adjustForRepeats(wordString)
  2600.                     speech.speak(wordString)
  2601.                 
  2602.         
  2603.         self.updateBrailleReview(targetCursorCell)
  2604.         return True
  2605.  
  2606.     
  2607.     def reviewCurrentAccessible(self, inputEvent):
  2608.         context = self.getFlatReviewContext()
  2609.         (zoneString, x, y, width, height) = context.getCurrent(flat_review.Context.ZONE)
  2610.         self.drawOutline(x, y, width, height)
  2611.         if not isinstance(inputEvent, input_event.BrailleEvent):
  2612.             utterances = self.speechGenerator.getSpeech(context.getCurrentAccessible(), False)
  2613.             utterances.extend(self.tutorialGenerator.getTutorial(context.getCurrentAccessible(), False))
  2614.             speech.speakUtterances(utterances)
  2615.         
  2616.         return True
  2617.  
  2618.     
  2619.     def reviewPreviousItem(self, inputEvent):
  2620.         '''Moves the flat review context to the previous item.  Places
  2621.         the flat review cursor at the beginning of the item.'''
  2622.         context = self.getFlatReviewContext()
  2623.         moved = context.goPrevious(flat_review.Context.WORD, flat_review.Context.WRAP_LINE)
  2624.         if moved:
  2625.             self._reviewCurrentItem(inputEvent)
  2626.             self.targetCursorCell = braille.cursorCell
  2627.         
  2628.         return True
  2629.  
  2630.     
  2631.     def reviewNextItem(self, inputEvent):
  2632.         '''Moves the flat review context to the next item.  Places
  2633.         the flat review cursor at the beginning of the item.'''
  2634.         context = self.getFlatReviewContext()
  2635.         moved = context.goNext(flat_review.Context.WORD, flat_review.Context.WRAP_LINE)
  2636.         if moved:
  2637.             self._reviewCurrentItem(inputEvent)
  2638.             self.targetCursorCell = braille.cursorCell
  2639.         
  2640.         return True
  2641.  
  2642.     
  2643.     def reviewCurrentCharacter(self, inputEvent):
  2644.         '''Brailles and speaks the current flat review character.'''
  2645.         self._reviewCurrentCharacter(inputEvent, 1)
  2646.         self.lastReviewCurrentEvent = inputEvent
  2647.         return True
  2648.  
  2649.     
  2650.     def reviewSpellCurrentCharacter(self, inputEvent):
  2651.         """Brailles and 'spells' (phonetically) the current flat review
  2652.         character.
  2653.         """
  2654.         self._reviewCurrentCharacter(inputEvent, 2)
  2655.         self.lastReviewCurrentEvent = inputEvent
  2656.         return True
  2657.  
  2658.     
  2659.     def _reviewCurrentCharacter(self, inputEvent, speechType = 1):
  2660.         '''Presents the current flat review character via braille and speech.
  2661.  
  2662.         Arguments:
  2663.         - inputEvent - the current input event.
  2664.         - speechType - the desired presentation: speak (1) or phonetic (2)
  2665.         '''
  2666.         context = self.getFlatReviewContext()
  2667.         (charString, x, y, width, height) = context.getCurrent(flat_review.Context.CHAR)
  2668.         self.drawOutline(x, y, width, height)
  2669.         if not isinstance(inputEvent, input_event.BrailleEvent):
  2670.             if not charString or not len(charString):
  2671.                 speech.speak(_('blank'))
  2672.             else:
  2673.                 (lineString, x, y, width, height) = context.getCurrent(flat_review.Context.LINE)
  2674.                 if lineString == '\n':
  2675.                     speech.speak(_('blank'))
  2676.                 elif speechType == 2:
  2677.                     self.phoneticSpellCurrentItem(charString)
  2678.                 elif charString.decode('UTF-8').isupper():
  2679.                     speech.speakCharacter(charString, self.voices[settings.UPPERCASE_VOICE])
  2680.                 else:
  2681.                     speech.speakCharacter(charString)
  2682.         
  2683.         self.updateBrailleReview()
  2684.         return True
  2685.  
  2686.     
  2687.     def reviewPreviousCharacter(self, inputEvent):
  2688.         '''Moves the flat review context to the previous character.  Places
  2689.         the flat review cursor at character.'''
  2690.         context = self.getFlatReviewContext()
  2691.         moved = context.goPrevious(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE)
  2692.         if moved:
  2693.             self._reviewCurrentCharacter(inputEvent)
  2694.             self.targetCursorCell = braille.cursorCell
  2695.         
  2696.         return True
  2697.  
  2698.     
  2699.     def reviewEndOfLine(self, inputEvent):
  2700.         '''Moves the flat review context to the end of the line.  Places
  2701.         the flat review cursor at the end of the line.'''
  2702.         context = self.getFlatReviewContext()
  2703.         context.goEnd(flat_review.Context.LINE)
  2704.         self.reviewCurrentCharacter(inputEvent)
  2705.         self.targetCursorCell = braille.cursorCell
  2706.         return True
  2707.  
  2708.     
  2709.     def reviewNextCharacter(self, inputEvent):
  2710.         '''Moves the flat review context to the next character.  Places
  2711.         the flat review cursor at character.'''
  2712.         context = self.getFlatReviewContext()
  2713.         moved = context.goNext(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE)
  2714.         if moved:
  2715.             self._reviewCurrentCharacter(inputEvent)
  2716.             self.targetCursorCell = braille.cursorCell
  2717.         
  2718.         return True
  2719.  
  2720.     
  2721.     def reviewAbove(self, inputEvent):
  2722.         '''Moves the flat review context to the character most directly
  2723.         above the current flat review cursor.  Places the flat review
  2724.         cursor at character.'''
  2725.         context = self.getFlatReviewContext()
  2726.         moved = context.goAbove(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE)
  2727.         if moved:
  2728.             self._reviewCurrentItem(inputEvent, self.targetCursorCell)
  2729.         
  2730.         return True
  2731.  
  2732.     
  2733.     def reviewBelow(self, inputEvent):
  2734.         '''Moves the flat review context to the character most directly
  2735.         below the current flat review cursor.  Places the flat review
  2736.         cursor at character.'''
  2737.         context = self.getFlatReviewContext()
  2738.         moved = context.goBelow(flat_review.Context.CHAR, flat_review.Context.WRAP_LINE)
  2739.         if moved:
  2740.             self._reviewCurrentItem(inputEvent, self.targetCursorCell)
  2741.         
  2742.         return True
  2743.  
  2744.     
  2745.     def showZones(self, inputEvent):
  2746.         '''Debug routine to paint rectangles around the discrete
  2747.         interesting (e.g., text)  zones in the active window for
  2748.         this application.
  2749.         '''
  2750.         flatReviewContext = self.getFlatReviewContext()
  2751.         lines = flatReviewContext.lines
  2752.         for line in lines:
  2753.             lineString = ''
  2754.             for zone in line.zones:
  2755.                 lineString += " '%s' [%s]" % (zone.string, zone.accessible.getRoleName())
  2756.             
  2757.             debug.println(debug.LEVEL_OFF, lineString)
  2758.         
  2759.         self.flatReviewContext = None
  2760.  
  2761.     
  2762.     def find(self, query = None):
  2763.         '''Searches for the specified query.  If no query is specified,
  2764.         it searches for the query specified in the Orca Find dialog.
  2765.  
  2766.         Arguments:
  2767.         - query: The search query to find.
  2768.         '''
  2769.         if not query:
  2770.             query = find.getLastQuery()
  2771.         
  2772.         if query:
  2773.             context = self.getFlatReviewContext()
  2774.             location = query.findQuery(context, self.justEnteredFlatReviewMode)
  2775.             if not location:
  2776.                 message = _('string not found')
  2777.                 braille.displayMessage(message)
  2778.                 speech.speak(message)
  2779.             else:
  2780.                 context.setCurrent(location.lineIndex, location.zoneIndex, location.wordIndex, location.charIndex)
  2781.                 self.reviewCurrentItem(None)
  2782.                 self.targetCursorCell = braille.cursorCell
  2783.         
  2784.  
  2785.     
  2786.     def findNext(self, inputEvent):
  2787.         '''Searches forward for the next instance of the string
  2788.         searched for via the Orca Find dialog.  Other than direction
  2789.         and the starting point, the search options initially specified
  2790.         (case sensitivity, window wrap, and full/partial match) are
  2791.         preserved.
  2792.         '''
  2793.         lastQuery = find.getLastQuery()
  2794.         if lastQuery:
  2795.             lastQuery.searchBackwards = False
  2796.             lastQuery.startAtTop = False
  2797.             self.find(lastQuery)
  2798.         else:
  2799.             orca.showFindGUI()
  2800.  
  2801.     
  2802.     def findPrevious(self, inputEvent):
  2803.         '''Searches backwards for the next instance of the string
  2804.         searched for via the Orca Find dialog.  Other than direction
  2805.         and the starting point, the search options initially specified
  2806.         (case sensitivity, window wrap, and full/or partial match) are
  2807.         preserved.
  2808.         '''
  2809.         lastQuery = find.getLastQuery()
  2810.         if lastQuery:
  2811.             lastQuery.searchBackwards = True
  2812.             lastQuery.startAtTop = False
  2813.             self.find(lastQuery)
  2814.         else:
  2815.             orca.showFindGUI()
  2816.  
  2817.     
  2818.     def goToBookmark(self, inputEvent):
  2819.         ''' Go to the bookmark indexed by inputEvent.hw_code.  Delegates to
  2820.         Bookmark.goToBookmark '''
  2821.         bookmarks = self.getBookmarks()
  2822.         bookmarks.goToBookmark(inputEvent)
  2823.  
  2824.     
  2825.     def addBookmark(self, inputEvent):
  2826.         ''' Add an in-page accessible object bookmark for this key.
  2827.         Delegates to Bookmark.addBookmark '''
  2828.         bookmarks = self.getBookmarks()
  2829.         bookmarks.addBookmark(inputEvent)
  2830.  
  2831.     
  2832.     def bookmarkCurrentWhereAmI(self, inputEvent):
  2833.         ''' Report "Where am I" information for this bookmark relative to the
  2834.         current pointer location.  Delegates to
  2835.         Bookmark.bookmarkCurrentWhereAmI'''
  2836.         bookmarks = self.getBookmarks()
  2837.         bookmarks.bookmarkCurrentWhereAmI(inputEvent)
  2838.  
  2839.     
  2840.     def saveBookmarks(self, inputEvent):
  2841.         ''' Save the bookmarks for this script. Delegates to
  2842.         Bookmark.saveBookmarks '''
  2843.         bookmarks = self.getBookmarks()
  2844.         bookmarks.saveBookmarks(inputEvent)
  2845.  
  2846.     
  2847.     def goToNextBookmark(self, inputEvent):
  2848.         ''' Go to the next bookmark location.  If no bookmark has yet to be
  2849.         selected, the first bookmark will be used.  Delegates to
  2850.         Bookmark.goToNextBookmark '''
  2851.         bookmarks = self.getBookmarks()
  2852.         bookmarks.goToNextBookmark(inputEvent)
  2853.  
  2854.     
  2855.     def goToPrevBookmark(self, inputEvent):
  2856.         ''' Go to the previous bookmark location.  If no bookmark has yet to
  2857.         be selected, the first bookmark will be used.  Delegates to
  2858.         Bookmark.goToPrevBookmark '''
  2859.         bookmarks = self.getBookmarks()
  2860.         bookmarks.goToPrevBookmark(inputEvent)
  2861.  
  2862.     
  2863.     def _isInterestingObj(self, obj):
  2864.         import inspect
  2865.         interesting = False
  2866.         if getattr(obj, '__class__', None):
  2867.             name = obj.__class__.__name__
  2868.             if name not in ('function', 'type', 'list', 'dict', 'tuple', 'wrapper_descriptor', 'module', 'method_descriptor', 'member_descriptor', 'instancemethod', 'builtin_function_or_method', 'frame', 'classmethod', 'classmethod_descriptor', '_Environ', 'MemoryError', '_Printer', '_Helper', 'getset_descriptor', 'weakref', 'property', 'cell', 'staticmethod', 'EventListener', 'KeystrokeListener', 'KeyBinding', 'InputEventHandler', 'Rolename'):
  2869.                 
  2870.                 try:
  2871.                     filename = inspect.getabsfile(obj.__class__)
  2872.                     if filename.index('orca'):
  2873.                         interesting = True
  2874.  
  2875.             
  2876.         
  2877.         return interesting
  2878.  
  2879.     
  2880.     def _detectCycle(self, obj, visitedObjs, indent = ''):
  2881.         '''Attempts to discover a cycle in object references.'''
  2882.         import gc
  2883.         visitedObjs.append(obj)
  2884.         for referent in gc.get_referents(obj):
  2885.             
  2886.             try:
  2887.                 if visitedObjs.index(referent):
  2888.                     if self._isInterestingObj(referent):
  2889.                         print indent, 'CYCLE!!!!', `referent`
  2890.                     
  2891.                     break
  2892.             except:
  2893.                 pass
  2894.  
  2895.             self._detectCycle(referent, visitedObjs, ' ' + indent)
  2896.         
  2897.         visitedObjs.remove(obj)
  2898.  
  2899.     
  2900.     def _printObjInfo(self, indent, obj):
  2901.         '''Prints information about an object, if we care about it.'''
  2902.         if self._isInterestingObj(obj):
  2903.             print indent, obj.__class__.__name__, `obj`
  2904.         
  2905.  
  2906.     
  2907.     def printMemoryUsageHandler(self, inputEvent):
  2908.         '''Prints memory usage information.'''
  2909.         print 'TODO: print something useful for memory debugging'
  2910.  
  2911.     
  2912.     def printAppsHandler(self, inputEvent = None):
  2913.         '''Prints a list of all applications to stdout.'''
  2914.         self.printApps()
  2915.         return True
  2916.  
  2917.     
  2918.     def printActiveAppHandler(self, inputEvent = None):
  2919.         '''Prints the currently active application.'''
  2920.         self.printActiveApp()
  2921.         return True
  2922.  
  2923.     
  2924.     def printAncestryHandler(self, inputEvent = None):
  2925.         '''Prints the ancestry for the current locusOfFocus'''
  2926.         self.printAncestry(orca_state.locusOfFocus)
  2927.         return True
  2928.  
  2929.     
  2930.     def printHierarchyHandler(self, inputEvent = None):
  2931.         '''Prints the application for the current locusOfFocus'''
  2932.         if orca_state.locusOfFocus:
  2933.             self.printHierarchy(orca_state.locusOfFocus.getApplication(), orca_state.locusOfFocus)
  2934.         
  2935.         return True
  2936.  
  2937.     
  2938.     def isSameObject(self, obj1, obj2):
  2939.         if obj1 == obj2:
  2940.             return True
  2941.         if not obj1 or not obj2:
  2942.             return False
  2943.         
  2944.         try:
  2945.             if obj1.name != obj2.name or obj1.getRole() != obj2.getRole():
  2946.                 return False
  2947.             extents1 = obj1.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
  2948.             extents2 = obj2.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
  2949.             if extents1.x == extents2.x and extents1.y == extents2.y and extents1.width == extents2.width and extents1.height == extents2.height:
  2950.                 return True
  2951.             parent1 = obj1
  2952.             parent2 = obj2
  2953.             while parent1 and parent2 and parent1.getState().contains(pyatspi.STATE_TRANSIENT) and parent2.getState().contains(pyatspi.STATE_TRANSIENT):
  2954.                 if parent1.getIndexInParent() != parent2.getIndexInParent():
  2955.                     return False
  2956.                 parent1 = parent1.parent
  2957.                 parent2 = parent2.parent
  2958.                 continue
  2959.                 parent1.getIndexInParent() != parent2.getIndexInParent()
  2960.             if parent1 and parent2 and parent1 == parent2:
  2961.                 return self.getRealActiveDescendant(obj1).name == self.getRealActiveDescendant(obj2).name
  2962.         except:
  2963.             not obj2
  2964.             obj1 == obj2
  2965.  
  2966.         
  2967.         try:
  2968.             parent1 = obj1
  2969.             parent2 = obj2
  2970.             while parent1 and parent2 and parent1.getRole() == pyatspi.ROLE_LABEL and parent2.getRole() == pyatspi.ROLE_LABEL:
  2971.                 if parent1.getIndexInParent() != parent2.getIndexInParent():
  2972.                     return False
  2973.                 parent1 = parent1.parent
  2974.                 parent2 = parent2.parent
  2975.                 continue
  2976.                 parent1.getIndexInParent() != parent2.getIndexInParent()
  2977.             if parent1 and parent2 and parent1 == parent2:
  2978.                 return True
  2979.         except:
  2980.             not obj2
  2981.             obj1 == obj2
  2982.  
  2983.         return False
  2984.  
  2985.     
  2986.     def appendString(self, text, newText, delimiter = ' '):
  2987.         '''Appends the newText to the given text with the delimiter in between
  2988.         and returns the new string.  Edge cases, such as no initial text or
  2989.         no newText, are handled gracefully.'''
  2990.         if not newText or len(newText) == 0:
  2991.             return text
  2992.         if text and len(text):
  2993.             return text + delimiter + newText
  2994.         return newText
  2995.  
  2996.     
  2997.     def __hasLabelForRelation(self, label):
  2998.         '''Check if label has a LABEL_FOR relation
  2999.  
  3000.         Arguments:
  3001.         - label: the label in question
  3002.  
  3003.         Returns TRUE if label has a LABEL_FOR relation.
  3004.         '''
  3005.         if not label or label.getRole() != pyatspi.ROLE_LABEL:
  3006.             return False
  3007.         relations = label.getRelationSet()
  3008.         for relation in relations:
  3009.             if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR:
  3010.                 return True
  3011.         
  3012.         return False
  3013.  
  3014.     
  3015.     def __isLabeling(self, label, obj):
  3016.         '''Check if label is connected via  LABEL_FOR relation with object
  3017.  
  3018.         Arguments:
  3019.         - obj: the object in question
  3020.         - labeled: the label in question
  3021.  
  3022.         Returns TRUE if label has a relation LABEL_FOR for object.
  3023.         '''
  3024.         if not obj and not label or label.getRole() != pyatspi.ROLE_LABEL:
  3025.             return False
  3026.         relations = label.getRelationSet()
  3027.         if not relations:
  3028.             return False
  3029.         for relation in relations:
  3030.             if relation.getRelationType() == pyatspi.RELATION_LABEL_FOR:
  3031.                 for i in range(0, relation.getNTargets()):
  3032.                     target = relation.getTarget(i)
  3033.                     if target == obj:
  3034.                         return True
  3035.                 
  3036.             target == obj
  3037.         
  3038.         return False
  3039.  
  3040.     
  3041.     def getUnicodeCurrencySymbols(self):
  3042.         '''Return a list of the unicode currency symbols, populating the list
  3043.         if this is the first time that this routine has been called.
  3044.  
  3045.         Returns a list of unicode currency symbols.
  3046.         '''
  3047.         if not self._unicodeCurrencySymbols:
  3048.             self._unicodeCurrencySymbols = [
  3049.                 u'$',
  3050.                 u'¬¢',
  3051.                 u'¬£',
  3052.                 u'¬§',
  3053.                 u'¬•',
  3054.                 u'Δí',
  3055.                 u'ÿã',
  3056.                 u'‡ß≤',
  3057.                 u'‡ß≥',
  3058.                 u'‡´±',
  3059.                 u'‡Øπ',
  3060.                 u'‡∏ø',
  3061.                 u'·üõ',
  3062.                 u'‚Ñ≥',
  3063.                 u'ÂÖÉ',
  3064.                 u'ÂÜÜ',
  3065.                 u'ÂúÜ',
  3066.                 u'Âúì',
  3067.                 u'Ô∑º']
  3068.             for ordChar in range(ord(u'‚dž'), ord(u'‚ǵ') + 1):
  3069.                 self._unicodeCurrencySymbols.append(unichr(ordChar))
  3070.             
  3071.         
  3072.         return self._unicodeCurrencySymbols
  3073.  
  3074.     
  3075.     def findDisplayedLabel(self, obj):
  3076.         '''Return a list of the objects that are labelling this object.
  3077.  
  3078.         Argument:
  3079.         - obj: the object in question
  3080.  
  3081.         Returns a list of the objects that are labelling this object.
  3082.         '''
  3083.         label = []
  3084.         relations = obj.getRelationSet()
  3085.         allTargets = []
  3086.         for relation in relations:
  3087.             if relation.getRelationType() == pyatspi.RELATION_LABELLED_BY:
  3088.                 for i in range(0, relation.getNTargets()):
  3089.                     target = relation.getTarget(i)
  3090.                     if target not in allTargets:
  3091.                         allTargets.append(target)
  3092.                         label.append(target)
  3093.                         continue
  3094.                 
  3095.         
  3096.         if not len(label):
  3097.             potentialLabels = []
  3098.             useLabel = False
  3099.             if obj.getRole() == pyatspi.ROLE_EMBEDDED:
  3100.                 candidate = obj
  3101.                 while candidate.childCount:
  3102.                     candidate = candidate[0]
  3103.                 candidate = candidate.parent
  3104.                 for child in candidate:
  3105.                     if child.getRole() == pyatspi.ROLE_FILLER:
  3106.                         candidate = child
  3107.                         break
  3108.                         continue
  3109.                 
  3110.                 for child in candidate:
  3111.                     if child.getRole() == pyatspi.ROLE_LABEL:
  3112.                         useLabel = True
  3113.                         potentialLabels.append(child)
  3114.                         continue
  3115.                 
  3116.             elif (obj.getRole() == pyatspi.ROLE_FILLER or obj.getRole() == pyatspi.ROLE_PANEL) and obj.childCount == 2:
  3117.                 (child0, child1) = obj
  3118.                 child0_role = child0.getRole()
  3119.                 child1_role = child1.getRole()
  3120.                 if child0_role == pyatspi.ROLE_LABEL and not self._Script__hasLabelForRelation(child0) and child1_role in [
  3121.                     pyatspi.ROLE_FILLER,
  3122.                     pyatspi.ROLE_PANEL]:
  3123.                     useLabel = True
  3124.                     potentialLabels.append(child0)
  3125.                 elif child1_role == pyatspi.ROLE_LABEL and not self._Script__hasLabelForRelation(child1) and child0_role in [
  3126.                     pyatspi.ROLE_FILLER,
  3127.                     pyatspi.ROLE_PANEL]:
  3128.                     useLabel = True
  3129.                     potentialLabels.append(child1)
  3130.                 
  3131.             else:
  3132.                 parent = obj.parent
  3133.                 if parent:
  3134.                     if parent.getRole() == pyatspi.ROLE_FILLER or parent.getRole() == pyatspi.ROLE_PANEL:
  3135.                         for potentialLabel in parent:
  3136.                             
  3137.                             try:
  3138.                                 useLabel = self._Script__isLabeling(potentialLabel, obj)
  3139.                                 if useLabel:
  3140.                                     potentialLabels.append(potentialLabel)
  3141.                                     break
  3142.                             continue
  3143.                             continue
  3144.  
  3145.                         
  3146.                     
  3147.             if useLabel and len(potentialLabels):
  3148.                 label = potentialLabels
  3149.             
  3150.         
  3151.         return label
  3152.  
  3153.     
  3154.     def getDisplayedLabel(self, obj):
  3155.         '''If there is an object labelling the given object, return the
  3156.         text being displayed for the object labelling this object.
  3157.         Otherwise, return None.
  3158.  
  3159.         Argument:
  3160.         - obj: the object in question
  3161.  
  3162.         Returns the string of the object labelling this object, or None
  3163.         if there is nothing of interest here.
  3164.         '''
  3165.         labelString = None
  3166.         labels = self.findDisplayedLabel(obj)
  3167.         for label in labels:
  3168.             labelString = self.appendString(labelString, self.getDisplayedText(label))
  3169.         
  3170.         return labelString
  3171.  
  3172.     
  3173.     def __getDisplayedTextInComboBox(self, combo):
  3174.         '''Returns the text being displayed in a combo box.  If nothing is
  3175.         displayed, then None is returned.
  3176.  
  3177.         Arguments:
  3178.         - combo: the combo box
  3179.  
  3180.         Returns the text in the combo box or an empty string if nothing is
  3181.         displayed.
  3182.         '''
  3183.         displayedText = None
  3184.         textObj = None
  3185.         for child in combo:
  3186.             if child and child.getRole() == pyatspi.ROLE_TEXT:
  3187.                 textObj = child
  3188.                 continue
  3189.         
  3190.         if textObj:
  3191.             (displayedText, caretOffset, startOffset) = self.getTextLineAtCaret(textObj)
  3192.         else:
  3193.             
  3194.             try:
  3195.                 comboSelection = combo.querySelection()
  3196.                 selectedItem = comboSelection.getSelectedChild(0)
  3197.             except:
  3198.                 selectedItem = None
  3199.  
  3200.             if selectedItem:
  3201.                 displayedText = self.getDisplayedText(selectedItem)
  3202.             elif combo.name and len(combo.name):
  3203.                 displayedText = combo.name
  3204.             else:
  3205.                 (displayedText, caretOffset, startOffset) = self.getTextLineAtCaret(combo)
  3206.                 if not displayedText:
  3207.                     pass
  3208.                 displayedText = None
  3209.         return displayedText
  3210.  
  3211.     
  3212.     def getDisplayedText(self, obj):
  3213.         """Returns the text being displayed for an object.
  3214.  
  3215.         Arguments:
  3216.         - obj: the object
  3217.  
  3218.         Returns the text being displayed for an object or None if there isn't
  3219.         any text being shown.
  3220.         """
  3221.         displayedText = None
  3222.         role = obj.getRole()
  3223.         if role == pyatspi.ROLE_COMBO_BOX:
  3224.             return self._Script__getDisplayedTextInComboBox(obj)
  3225.         
  3226.         try:
  3227.             text = obj.queryText()
  3228.         except NotImplementedError:
  3229.             role == pyatspi.ROLE_COMBO_BOX
  3230.             role == pyatspi.ROLE_COMBO_BOX
  3231.         except:
  3232.             role == pyatspi.ROLE_COMBO_BOX
  3233.  
  3234.         displayedText = text.getText(0, -1)
  3235.         unicodeText = displayedText.decode('UTF-8')
  3236.         if unicodeText and len(unicodeText) == 1 and unicodeText[0] == self.EMBEDDED_OBJECT_CHARACTER and obj.childCount > 0:
  3237.             
  3238.             try:
  3239.                 displayedText = self.getDisplayedText(obj[0])
  3240.             debug.printException(debug.LEVEL_WARNING)
  3241.  
  3242.         elif unicodeText:
  3243.             for i in range(0, len(unicodeText)):
  3244.                 if unicodeText[i] == self.EMBEDDED_OBJECT_CHARACTER:
  3245.                     displayedText = None
  3246.                     break
  3247.                     continue
  3248.                 role == pyatspi.ROLE_COMBO_BOX
  3249.             
  3250.         
  3251.         if not displayedText:
  3252.             displayedText = obj.name
  3253.         
  3254.         if not displayedText and role == pyatspi.ROLE_PUSH_BUTTON:
  3255.             for child in obj:
  3256.                 if child.getRole() == pyatspi.ROLE_LABEL:
  3257.                     childText = self.getDisplayedText(child)
  3258.                     if childText and len(childText):
  3259.                         displayedText = self.appendString(displayedText, childText)
  3260.                     
  3261.                 len(childText)
  3262.             
  3263.         
  3264.         return displayedText
  3265.  
  3266.     
  3267.     def getTextForValue(self, obj):
  3268.         """Returns the text to be displayed for the object's current value.
  3269.  
  3270.         Arguments:
  3271.         - obj: the Accessible object that may or may not have a value.
  3272.  
  3273.         Returns a string representing the value.
  3274.         """
  3275.         attributes = obj.getAttributes()
  3276.         for attribute in attributes:
  3277.             if attribute.startswith('valuetext'):
  3278.                 return attribute[10:]
  3279.         
  3280.         
  3281.         try:
  3282.             value = obj.queryValue()
  3283.         except NotImplementedError:
  3284.             attribute.startswith('valuetext')
  3285.             attribute.startswith('valuetext')
  3286.             return ''
  3287.  
  3288.         
  3289.         try:
  3290.             minimumIncrement = value.minimumIncrement
  3291.         except:
  3292.             attribute.startswith('valuetext')
  3293.             minimumIncrement = 0
  3294.  
  3295.         if minimumIncrement == 0:
  3296.             minimumIncrement = (value.maximumValue - value.minimumValue) / 100
  3297.         
  3298.         
  3299.         try:
  3300.             decimalPlaces = max(0, -math.log10(minimumIncrement))
  3301.         except:
  3302.             
  3303.             try:
  3304.                 decimalPlaces = max(0, -math.log10(value.minimumValue))
  3305.             try:
  3306.                 decimalPlaces = max(0, -math.log10(value.maximumValue))
  3307.             decimalPlaces = 0
  3308.  
  3309.         
  3310.  
  3311.         formatter = '%%.%df' % decimalPlaces
  3312.         valueString = formatter % value.currentValue
  3313.         return valueString
  3314.  
  3315.     
  3316.     def findFocusedObject(self, root):
  3317.         '''Returns the accessible that has focus under or including the
  3318.         given root.
  3319.  
  3320.         TODO: This will currently traverse all children, whether they are
  3321.         visible or not and/or whether they are children of parents that
  3322.         manage their descendants.  At some point, this method should be
  3323.         optimized to take such things into account.
  3324.  
  3325.         Arguments:
  3326.         - root: the root object where to start searching
  3327.  
  3328.         Returns the object with the FOCUSED state or None if no object with
  3329.         the FOCUSED state can be found.
  3330.         '''
  3331.         if root.getState().contains(pyatspi.STATE_FOCUSED):
  3332.             return root
  3333.         for child in root:
  3334.             
  3335.             try:
  3336.                 candidate = self.findFocusedObject(child)
  3337.                 if candidate:
  3338.                     return candidate
  3339.             continue
  3340.             continue
  3341.  
  3342.         
  3343.  
  3344.     
  3345.     def getRealActiveDescendant(self, obj):
  3346.         '''Given an object that should be a child of an object that
  3347.         manages its descendants, return the child that is the real
  3348.         active descendant carrying useful information.
  3349.  
  3350.         Arguments:
  3351.         - obj: an object that should be a child of an object that
  3352.         manages its descendants.
  3353.         '''
  3354.         if obj.getRole() == pyatspi.ROLE_TABLE_CELL and obj.childCount:
  3355.             nonTableCellFound = False
  3356.             for child in obj:
  3357.                 if child.getRole() != pyatspi.ROLE_TABLE_CELL:
  3358.                     nonTableCellFound = True
  3359.                     continue
  3360.             
  3361.             if not nonTableCellFound:
  3362.                 for child in obj:
  3363.                     
  3364.                     try:
  3365.                         text = child.queryText()
  3366.                     except NotImplementedError:
  3367.                         continue
  3368.                         continue
  3369.  
  3370.                     if text.getText(0, -1):
  3371.                         return child
  3372.                 
  3373.             
  3374.         
  3375.         if obj and obj.childCount:
  3376.             return obj[-1]
  3377.         return obj
  3378.  
  3379.     
  3380.     def isDesiredFocusedItem(self, obj, rolesList):
  3381.         """Called to determine if the given object and it's hierarchy of
  3382.            parent objects, each have the desired roles.
  3383.  
  3384.         Arguments:
  3385.         - obj: the accessible object to check.
  3386.         - rolesList: the list of desired roles for the components and the
  3387.           hierarchy of its parents.
  3388.  
  3389.         Returns True if all roles match.
  3390.         """
  3391.         current = obj
  3392.         for role in rolesList:
  3393.             if current is None:
  3394.                 return False
  3395.             if not isinstance(role, list):
  3396.                 role = [
  3397.                     role]
  3398.             
  3399.             if isinstance(role[0], str):
  3400.                 current_role = current.getRoleName()
  3401.             else:
  3402.                 current_role = current.getRole()
  3403.             if current_role not in role:
  3404.                 return False
  3405.             current = current.parent
  3406.         
  3407.         return True
  3408.  
  3409.     
  3410.     def speakMisspeltWord(self, allTokens, badWord):
  3411.         '''Called by various spell checking routine to speak the misspelt word,
  3412.            plus the context that it is being used in.
  3413.  
  3414.         Arguments:
  3415.         - allTokens: a list of all the words.
  3416.         - badWord: the misspelt word.
  3417.         '''
  3418.         for i in range(0, len(allTokens)):
  3419.             if allTokens[i].startswith(badWord):
  3420.                 minIndex = i - 5
  3421.                 if minIndex < 0:
  3422.                     minIndex = 0
  3423.                 
  3424.                 maxIndex = i + 5
  3425.                 if maxIndex > len(allTokens) - 1:
  3426.                     maxIndex = len(allTokens) - 1
  3427.                 
  3428.                 utterances = [
  3429.                     _('Misspelled word: %s') % badWord]
  3430.                 contextPhrase = ' '.join(allTokens[minIndex:maxIndex + 1])
  3431.                 utterances.append(_('Context is %s') % contextPhrase)
  3432.                 text = ' '.join(utterances)
  3433.                 speech.speak(text)
  3434.                 continue
  3435.         
  3436.  
  3437.     
  3438.     def textLines(self, obj):
  3439.         '''Creates a generator that can be used to iterate over each line
  3440.         of a text object, starting at the caret offset.
  3441.  
  3442.         Arguments:
  3443.         - obj: an Accessible that has a text specialization
  3444.  
  3445.         Returns an iterator that produces elements of the form:
  3446.         [SayAllContext, acss], where SayAllContext has the text to be
  3447.         spoken and acss is an ACSS instance for speaking the text.
  3448.         '''
  3449.         
  3450.         try:
  3451.             text = obj.queryText()
  3452.         except:
  3453.             return None
  3454.  
  3455.         length = text.characterCount
  3456.         offset = text.caretOffset
  3457.         if settings.sayAllStyle == settings.SAYALL_STYLE_SENTENCE:
  3458.             mode = pyatspi.TEXT_BOUNDARY_SENTENCE_END
  3459.         elif settings.sayAllStyle == settings.SAYALL_STYLE_LINE:
  3460.             mode = pyatspi.TEXT_BOUNDARY_LINE_START
  3461.         else:
  3462.             mode = pyatspi.TEXT_BOUNDARY_LINE_START
  3463.         done = False
  3464.         while not done:
  3465.             lastEndOffset = -1
  3466.             while offset < length:
  3467.                 (lineString, startOffset, endOffset) = text.getTextAtOffset(offset, mode)
  3468.                 if not lineString:
  3469.                     mode = pyatspi.TEXT_BOUNDARY_LINE_START
  3470.                     (lineString, startOffset, endOffset) = text.getTextAtOffset(offset, mode)
  3471.                 
  3472.                 if startOffset < 0:
  3473.                     break
  3474.                 
  3475.                 if endOffset == lastEndOffset:
  3476.                     offset = max(offset + 1, lastEndOffset + 1)
  3477.                     lastEndOffset = endOffset
  3478.                     continue
  3479.                 
  3480.                 lastEndOffset = endOffset
  3481.                 offset = endOffset
  3482.                 lineString = self.adjustForRepeats(lineString)
  3483.                 if lineString.isupper():
  3484.                     voice = settings.voices[settings.UPPERCASE_VOICE]
  3485.                 else:
  3486.                     voice = settings.voices[settings.DEFAULT_VOICE]
  3487.                 yield [
  3488.                     speechserver.SayAllContext(obj, lineString, startOffset, endOffset),
  3489.                     voice]
  3490.             moreLines = False
  3491.             relations = obj.getRelationSet()
  3492.             for relation in relations:
  3493.                 if relation.getRelationType() == pyatspi.RELATION_FLOWS_TO:
  3494.                     obj = relation.getTarget(0)
  3495.                     
  3496.                     try:
  3497.                         text = obj.queryText()
  3498.                     except NotImplementedError:
  3499.                         return None
  3500.  
  3501.                     length = text.characterCount
  3502.                     offset = 0
  3503.                     moreLines = True
  3504.                     break
  3505.                     continue
  3506.             
  3507.             if not moreLines:
  3508.                 done = True
  3509.                 continue
  3510.  
  3511.     
  3512.     def _addRepeatSegment(self, segment, line, respectPunctuation = True):
  3513.         '''Add in the latest line segment, adjusting for repeat characters
  3514.         and punctuation.
  3515.  
  3516.         Arguments:
  3517.         - segment: the segment of repeated characters.
  3518.         - line: the current built-up line to characters to speak.
  3519.         - respectPunctuation: if False, ignore punctuation level.
  3520.  
  3521.         Returns: the current built-up line plus the new segment, after
  3522.         adjusting for repeat character counts and punctuation.
  3523.         '''
  3524.         style = settings.verbalizePunctuationStyle
  3525.         isPunctChar = True
  3526.         
  3527.         try:
  3528.             (level, action) = punctuation_settings.getPunctuationInfo(segment[0])
  3529.         except:
  3530.             isPunctChar = False
  3531.  
  3532.         count = len(segment)
  3533.         if count >= settings.repeatCharacterLimit and segment[0] not in self.whitespace:
  3534.             if (not respectPunctuation or isPunctChar) and style <= level:
  3535.                 repeatChar = chnames.getCharacterName(segment[0])
  3536.                 line += ' ' + ngettext('%(count)d %(repeatChar)s character', '%(count)d %(repeatChar)s characters', count) % {
  3537.                     'count': count,
  3538.                     'repeatChar': repeatChar }
  3539.             else:
  3540.                 line += segment
  3541.         else:
  3542.             line += segment
  3543.         return line
  3544.  
  3545.     
  3546.     def adjustForLinks(self, obj, line, startOffset):
  3547.         '''Adjust line to include the word "link" after any hypertext links.
  3548.  
  3549.         Arguments:
  3550.         - obj: the accessible object that this line came from.
  3551.         - line: the string to adjust for links.
  3552.         - startOffset: the caret offset at the start of the line.
  3553.  
  3554.         Returns: a new line adjusted to add the speaking of "link" after
  3555.         text which is also a link.
  3556.         '''
  3557.         line = line.decode('UTF-8')
  3558.         endOffset = startOffset + len(line)
  3559.         
  3560.         try:
  3561.             hyperText = obj.queryHypertext()
  3562.             nLinks = hyperText.getNLinks()
  3563.         except:
  3564.             nLinks = 0
  3565.  
  3566.         adjustedLine = list(line)
  3567.         for n in range(nLinks, 0, -1):
  3568.             link = hyperText.getLink(n - 1)
  3569.             if link.endIndex < link.endIndex:
  3570.                 pass
  3571.             elif link.endIndex < endOffset:
  3572.                 index = link.endIndex - startOffset
  3573.             elif link.startIndex <= link.startIndex:
  3574.                 pass
  3575.             elif link.startIndex < endOffset:
  3576.                 index = len(line) - 1
  3577.             
  3578.             linkString = ' ' + _('link')
  3579.             nextChar = adjustedLine[index]
  3580.             if not nextChar in self.whitespace or punctuation_settings.getPunctuationInfo(nextChar):
  3581.                 linkString += ' '
  3582.             
  3583.             adjustedLine[index:index] = linkString
  3584.         
  3585.         return ''.join(adjustedLine).encode('UTF-8')
  3586.  
  3587.     
  3588.     def adjustForRepeats(self, line):
  3589.         """Adjust line to include repeat character counts.
  3590.         As some people will want this and others might not,
  3591.         there is a setting in settings.py that determines
  3592.         whether this functionality is enabled.
  3593.  
  3594.         repeatCharacterLimit = <n>
  3595.  
  3596.         If <n> is 0, then there would be no repeat characters.
  3597.         Otherwise <n> would be the number of same characters (or more)
  3598.         in a row that cause the repeat character count output.
  3599.         If the value is set to 1, 2 or 3 then it's treated as if it was
  3600.         zero. In other words, no repeat character count is given.
  3601.  
  3602.         Arguments:
  3603.         - line: the string to adjust for repeat character counts.
  3604.  
  3605.         Returns: a new line adjusted for repeat character counts (if enabled).
  3606.         """
  3607.         line = line.decode('UTF-8')
  3608.         if len(line) < 4 or settings.repeatCharacterLimit < 4:
  3609.             return line.encode('UTF-8')
  3610.         newLine = u''
  3611.         multipleChars = False
  3612.         for i in range(1, len(line)):
  3613.             lastChar = line[i]
  3614.         
  3615.         newLine = self._addRepeatSegment(segment, newLine, multipleChars)
  3616.         return newLine.encode('UTF-8')
  3617.  
  3618.     
  3619.     def _getPronunciationForSegment(self, segment):
  3620.         '''Adjust the word segment to potentially replace it with what
  3621.         those words actually sound like. Two pronunciation dictionaries
  3622.         are checked. First the application specific one (which might be
  3623.         empty), then the default (global) one.
  3624.  
  3625.         Arguments:
  3626.         - segment: the string to adjust for words in the pronunciation
  3627.           dictionaries.
  3628.  
  3629.         Returns: a new word segment adjusted for words found in the
  3630.         pronunciation dictionaries, or the original word segment if there
  3631.         was no dictionary entry.
  3632.         '''
  3633.         newSegment = pronunciation_dict.getPronunciation(segment, self.app_pronunciation_dict)
  3634.         if newSegment == segment:
  3635.             newSegment = pronunciation_dict.getPronunciation(segment)
  3636.         
  3637.         return newSegment
  3638.  
  3639.     
  3640.     def adjustForPronunciation(self, line):
  3641.         '''Adjust the line to replace words in the pronunciation dictionary,
  3642.         with what those words actually sound like.
  3643.  
  3644.         Arguments:
  3645.         - line: the string to adjust for words in the pronunciation dictionary.
  3646.  
  3647.         Returns: a new line adjusted for words found in the pronunciation
  3648.         dictionary.
  3649.         '''
  3650.         line = line.decode('UTF-8')
  3651.         newLine = segment = u''
  3652.         for i in range(0, len(line)):
  3653.             if self.isWordDelimiter(line[i]):
  3654.                 if len(segment) != 0:
  3655.                     newLine = newLine + self._getPronunciationForSegment(segment)
  3656.                 
  3657.                 newLine = newLine + line[i]
  3658.                 segment = u''
  3659.                 continue
  3660.             segment += line[i]
  3661.         
  3662.         if len(segment) != 0:
  3663.             newLine = newLine + self._getPronunciationForSegment(segment)
  3664.         
  3665.         return newLine.encode('UTF-8')
  3666.  
  3667.     
  3668.     def getLinkIndex(self, obj, characterIndex):
  3669.         '''A brute force method to see if an offset is a link.  This
  3670.         is provided because not all Accessible Hypertext implementations
  3671.         properly support the getLinkIndex method.  Returns an index of
  3672.         0 or greater of the characterIndex is on a hyperlink.
  3673.  
  3674.         Arguments:
  3675.         -obj: the Accessible object with the Accessible Hypertext specialization
  3676.         -characterIndex: the text position to check
  3677.         '''
  3678.         if not obj:
  3679.             return -1
  3680.         
  3681.         try:
  3682.             obj.queryText()
  3683.         except NotImplementedError:
  3684.             obj
  3685.             obj
  3686.             return -1
  3687.  
  3688.         
  3689.         try:
  3690.             hypertext = obj.queryHypertext()
  3691.         except NotImplementedError:
  3692.             obj
  3693.             obj
  3694.             return -1
  3695.  
  3696.         for i in xrange(hypertext.getNLinks()):
  3697.             link = hypertext.getLink(i)
  3698.             if characterIndex >= link.startIndex and characterIndex <= link.endIndex:
  3699.                 return i
  3700.         
  3701.         return -1
  3702.  
  3703.     
  3704.     def getCellIndex(self, obj):
  3705.         '''Returns the index of the cell which should be used with the
  3706.         table interface.  This is necessary because in some apps we
  3707.         cannot count on getIndexInParent() returning the index we need.
  3708.  
  3709.         Arguments:
  3710.         -obj: the table cell whose index we need.
  3711.         '''
  3712.         return obj.getIndexInParent()
  3713.  
  3714.     
  3715.     def isSentenceDelimiter(self, currentChar, previousChar):
  3716.         '''Returns True if we are positioned at the end of a sentence.
  3717.         This is determined by checking if the current character is a 
  3718.         white space character and the previous character is one of the 
  3719.         normal end-of-sentence punctuation characters.
  3720.  
  3721.         Arguments:
  3722.         - currentChar:  the current character
  3723.         - previousChar: the previous character
  3724.  
  3725.         Returns True if the given character is a sentence delimiter.
  3726.         '''
  3727.         if not isinstance(currentChar, unicode):
  3728.             currentChar = currentChar.decode('UTF-8')
  3729.         
  3730.         if not isinstance(previousChar, unicode):
  3731.             previousChar = previousChar.decode('UTF-8')
  3732.         
  3733.         if currentChar == '\r' or currentChar == '\n':
  3734.             return True
  3735.         if currentChar in self.whitespace:
  3736.             pass
  3737.         return previousChar in '!.?:;'
  3738.  
  3739.     
  3740.     def isWordDelimiter(self, character):
  3741.         '''Returns True if the given character is a word delimiter.
  3742.  
  3743.         Arguments:
  3744.         - character: the character in question
  3745.  
  3746.         Returns True if the given character is a word delimiter.
  3747.         '''
  3748.         if not isinstance(character, unicode):
  3749.             character = character.decode('UTF-8')
  3750.         
  3751.         if not character in self.whitespace and character in '!*+,-./:;<=>?@[\\]^_{|}':
  3752.             pass
  3753.         return character == self.NO_BREAK_SPACE_CHARACTER
  3754.  
  3755.     
  3756.     def getFrame(self, obj):
  3757.         '''Returns the frame containing this object, or None if this object
  3758.         is not inside a frame.
  3759.  
  3760.         Arguments:
  3761.         - obj: the Accessible object
  3762.         '''
  3763.         if not 'Finding frame for source.name=' + obj.name:
  3764.             pass
  3765.         debug.println(debug.LEVEL_FINEST, 'None')
  3766.         while obj and obj != obj.parent and obj.getRole() != pyatspi.ROLE_FRAME:
  3767.             obj = obj.parent
  3768.             if obj:
  3769.                 if not '--> obj.name=' + obj.name:
  3770.                     pass
  3771.                 debug.println(debug.LEVEL_FINEST, 'None')
  3772.                 continue
  3773.         if obj and obj.getRole() == pyatspi.ROLE_FRAME:
  3774.             pass
  3775.         else:
  3776.             obj = None
  3777.         return obj
  3778.  
  3779.     
  3780.     def getTopLevel(self, obj):
  3781.         '''Returns the top-level object (frame, dialog ...) containing this
  3782.         object, or None if this object is not inside a top-level object.
  3783.  
  3784.         Arguments:
  3785.         - obj: the Accessible object
  3786.         '''
  3787.         if not 'Finding top-level object for source.name=' + obj.name:
  3788.             pass
  3789.         debug.println(debug.LEVEL_FINEST, 'None')
  3790.         while obj and obj.parent and obj != obj.parent and obj.parent.getRole() != pyatspi.ROLE_APPLICATION:
  3791.             obj = obj.parent
  3792.             if not '--> obj.name=' + obj.name:
  3793.                 pass
  3794.             debug.println(debug.LEVEL_FINEST, 'None')
  3795.         if obj and obj.parent and obj.parent.getRole() == pyatspi.ROLE_APPLICATION:
  3796.             pass
  3797.         else:
  3798.             obj = None
  3799.         return obj
  3800.  
  3801.     
  3802.     def getTopLevelName(self, obj):
  3803.         ''' Returns the name of the top-level object. See getTopLevel.
  3804.         '''
  3805.         top = self.getTopLevel(obj)
  3806.         if not top or not (top.name):
  3807.             return ''
  3808.         return top.name
  3809.  
  3810.     
  3811.     def getTextLineAtCaret(self, obj, offset = None):
  3812.         '''Gets the line of text where the caret is.
  3813.  
  3814.         Argument:
  3815.         - obj: an Accessible object that implements the AccessibleText
  3816.           interface
  3817.         - offset: an optional caret offset to use. (Not used here at the
  3818.           moment, but needed in the Gecko script)
  3819.  
  3820.         Returns the [string, caretOffset, startOffset] for the line of text
  3821.         where the caret is.
  3822.         '''
  3823.         
  3824.         try:
  3825.             text = obj.queryText()
  3826.         except NotImplementedError:
  3827.             return [
  3828.                 '',
  3829.                 0,
  3830.                 0]
  3831.  
  3832.         if text.caretOffset == text.characterCount:
  3833.             caretOffset = max(0, text.caretOffset - 1)
  3834.             character = text.getText(caretOffset, caretOffset + 1).decode('UTF-8')
  3835.         else:
  3836.             caretOffset = text.caretOffset
  3837.             character = None
  3838.         if text.caretOffset == text.characterCount and character == '\n':
  3839.             content = ''
  3840.             startOffset = caretOffset
  3841.         elif text.characterCount == 1:
  3842.             lineString = text.getText(caretOffset, caretOffset + 1)
  3843.             startOffset = caretOffset
  3844.         else:
  3845.             (lineString, startOffset, endOffset) = text.getTextAtOffset(caretOffset, pyatspi.TEXT_BOUNDARY_LINE_START)
  3846.         content = lineString.decode('UTF-8')
  3847.         if content[-1:] == '\n':
  3848.             content = content[:-1]
  3849.         
  3850.         return [
  3851.             content.encode('UTF-8'),
  3852.             text.caretOffset,
  3853.             startOffset]
  3854.  
  3855.     
  3856.     def getNodeLevel(self, obj):
  3857.         '''Determines the node level of this object if it is in a tree
  3858.         relation, with 0 being the top level node.  If this object is
  3859.         not in a tree relation, then -1 will be returned.
  3860.  
  3861.         Arguments:
  3862.         -obj: the Accessible object
  3863.         '''
  3864.         if not obj:
  3865.             return -1
  3866.         nodes = []
  3867.         node = obj
  3868.         done = False
  3869.         while not done:
  3870.             relations = node.getRelationSet()
  3871.             node = None
  3872.             for relation in relations:
  3873.                 if relation.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF:
  3874.                     node = relation.getTarget(0)
  3875.                     break
  3876.                     continue
  3877.                 obj
  3878.             
  3879.             if len(nodes) > 100 or nodes.count(node):
  3880.                 debug.println(debug.LEVEL_WARNING, 'Script.getNodeLevel detected a cycle!!!')
  3881.                 done = True
  3882.                 continue
  3883.             if node:
  3884.                 nodes.append(node)
  3885.                 debug.println(debug.LEVEL_FINEST, 'Script.getNodeLevel %d' % len(nodes))
  3886.                 continue
  3887.             done = True
  3888.         return len(nodes) - 1
  3889.  
  3890.     
  3891.     def getChildNodes(self, obj):
  3892.         '''Gets all of the children that have RELATION_NODE_CHILD_OF pointing
  3893.         to this expanded table cell.
  3894.  
  3895.         Arguments:
  3896.         -obj: the Accessible Object
  3897.  
  3898.         Returns: a list of all the child nodes
  3899.         '''
  3900.         
  3901.         try:
  3902.             table = obj.parent.queryTable()
  3903.         except:
  3904.             return []
  3905.  
  3906.         if not obj.getState().contains(pyatspi.STATE_EXPANDED):
  3907.             return []
  3908.         nodes = []
  3909.         index = self.getCellIndex(obj)
  3910.         row = table.getRowAtIndex(index)
  3911.         col = table.getColumnAtIndex(index)
  3912.         nodeLevel = self.getNodeLevel(obj)
  3913.         done = False
  3914.         for i in range(row + 1, table.nRows):
  3915.             cell = table.getAccessibleAt(i, col)
  3916.             relations = cell.getRelationSet()
  3917.             for relation in relations:
  3918.                 if relation.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF:
  3919.                     nodeOf = relation.getTarget(0)
  3920.                     if self.isSameObject(obj, nodeOf):
  3921.                         nodes.append(cell)
  3922.                     else:
  3923.                         currentLevel = self.getNodeLevel(nodeOf)
  3924.                         if currentLevel <= nodeLevel:
  3925.                             done = True
  3926.                         
  3927.                     break
  3928.                     continue
  3929.             
  3930.             if done:
  3931.                 break
  3932.                 continue
  3933.         
  3934.         return nodes
  3935.  
  3936.     
  3937.     def getKeyBinding(self, obj):
  3938.         '''Gets the mnemonic, accelerator string and possibly shortcut
  3939.         for the given object.  These are based upon the first accessible
  3940.         action for the object.
  3941.  
  3942.         Arguments:
  3943.         - obj: the Accessible object
  3944.  
  3945.         Returns: list containing strings: [mnemonic, shortcut, accelerator]
  3946.         '''
  3947.         
  3948.         try:
  3949.             action = obj.queryAction()
  3950.         except NotImplementedError:
  3951.             return [
  3952.                 '',
  3953.                 '',
  3954.                 '']
  3955.  
  3956.         bindingStrings = action.getKeyBinding(0).decode('UTF-8').split(';')
  3957.         if len(bindingStrings) == 3:
  3958.             mnemonic = bindingStrings[0]
  3959.             fullShortcut = bindingStrings[1]
  3960.             accelerator = bindingStrings[2]
  3961.         elif len(bindingStrings) > 0:
  3962.             mnemonic = ''
  3963.             fullShortcut = bindingStrings[0]
  3964.             
  3965.             try:
  3966.                 accelerator = bindingStrings[1]
  3967.             accelerator = ''
  3968.  
  3969.         else:
  3970.             mnemonic = ''
  3971.             fullShortcut = ''
  3972.             accelerator = ''
  3973.         fullShortcut = fullShortcut.replace('<', '')
  3974.         fullShortcut = fullShortcut.replace('>', ' ')
  3975.         fullShortcut = fullShortcut.replace(':', ' ').strip()
  3976.         if mnemonic.endswith(' '):
  3977.             mnemonic += _('space')
  3978.         
  3979.         mnemonic = mnemonic.replace('<', '')
  3980.         mnemonic = mnemonic.replace('>', ' ').strip()
  3981.         if accelerator.endswith(' '):
  3982.             accelerator += _('space')
  3983.         
  3984.         accelerator = accelerator.replace('<', '')
  3985.         accelerator = accelerator.replace('>', ' ').strip()
  3986.         debug.println(debug.LEVEL_FINEST, 'default.getKeyBinding: ' + repr([
  3987.             mnemonic,
  3988.             fullShortcut,
  3989.             accelerator]))
  3990.         return [
  3991.             mnemonic,
  3992.             fullShortcut,
  3993.             accelerator]
  3994.  
  3995.     
  3996.     def getKnownApplications(self):
  3997.         '''Retrieves the list of currently running apps for the desktop
  3998.         as a list of Accessible objects.
  3999.         '''
  4000.         debug.println(debug.LEVEL_FINEST, 'Script.getKnownApplications...')
  4001.         apps = filter((lambda x: x is not None), pyatspi.Registry.getDesktop(0))
  4002.         debug.println(debug.LEVEL_FINEST, '...Script.getKnownApplications')
  4003.         return apps
  4004.  
  4005.     
  4006.     def getObjects(self, root, onlyShowing = True):
  4007.         '''Returns a list of all objects under the given root.  Objects
  4008.         are returned in no particular order - this function does a simple
  4009.         tree traversal, ignoring the children of objects which report the
  4010.         MANAGES_DESCENDANTS state.
  4011.  
  4012.         Arguments:
  4013.         - root:        the Accessible object to traverse
  4014.         - onlyShowing: examine only those objects that are SHOWING
  4015.  
  4016.         Returns: a list of all objects under the specified object
  4017.         '''
  4018.         objlist = []
  4019.         if root.childCount <= 0:
  4020.             return objlist
  4021.         for i, child in enumerate(root):
  4022.             debug.println(debug.LEVEL_FINEST, 'Script.getObjects looking at child %d' % i)
  4023.             if child:
  4024.                 if (not onlyShowing or onlyShowing) and child.getState().contains(pyatspi.STATE_SHOWING):
  4025.                     objlist.append(child)
  4026.                     if child.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) == 0 and child.childCount > 0:
  4027.                         objlist.extend(self.getObjects(child, onlyShowing))
  4028.                     
  4029.             child.childCount > 0
  4030.         
  4031.         return objlist
  4032.  
  4033.     
  4034.     def findByRole(self, root, role, onlyShowing = True):
  4035.         """Returns a list of all objects of a specific role beneath the
  4036.         given root.  [[[TODO: MM - This is very inefficient - this should
  4037.         do it's own traversal and not add objects to the list that aren't
  4038.         of the specified role.  Instead it uses the traversal from
  4039.         getObjects and then deletes objects from the list that aren't of
  4040.         the specified role.  Logged as bugzilla bug 319740.]]]
  4041.  
  4042.         Arguments:
  4043.         - root the Accessible object to traverse
  4044.         - role the string describing the Accessible role of the object
  4045.         - onlyShowing: examine only those objects that are SHOWING
  4046.  
  4047.         Returns a list of descendants of the root that are of the given role.
  4048.         """
  4049.         objlist = []
  4050.         allobjs = self.getObjects(root, onlyShowing)
  4051.         for o in allobjs:
  4052.             if o.getRole() == role:
  4053.                 objlist.append(o)
  4054.                 continue
  4055.         
  4056.         return objlist
  4057.  
  4058.     
  4059.     def findUnrelatedLabels(self, root):
  4060.         '''Returns a list containing all the unrelated (i.e., have no
  4061.         relations to anything and are not a fundamental element of a
  4062.         more atomic component like a combo box) labels under the given
  4063.         root.  Note that the labels must also be showing on the display.
  4064.  
  4065.         Arguments:
  4066.         - root the Accessible object to traverse
  4067.  
  4068.         Returns a list of unrelated labels under the given root.
  4069.         '''
  4070.         allLabels = self.findByRole(root, pyatspi.ROLE_LABEL)
  4071.         unrelatedLabels = []
  4072.         for label in allLabels:
  4073.             relations = label.getRelationSet()
  4074.             if len(relations) == 0:
  4075.                 parent = label.parent
  4076.                 if parent and parent.getRole() == pyatspi.ROLE_PUSH_BUTTON:
  4077.                     pass
  4078.                 elif parent and parent.getRole() == pyatspi.ROLE_PANEL and parent.name == label.name:
  4079.                     pass
  4080.                 elif label.getState().contains(pyatspi.STATE_SHOWING):
  4081.                     unrelatedLabels.append(label)
  4082.                 
  4083.             parent.name == label.name
  4084.         
  4085.         sortedLabels = []
  4086.         for label in unrelatedLabels:
  4087.             label_extents = label.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
  4088.             index = 0
  4089.             for sortedLabel in sortedLabels:
  4090.                 sorted_extents = sortedLabel.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
  4091.                 if (label_extents.y > sorted_extents.y or label_extents.y == sorted_extents.y) and label_extents.x > sorted_extents.x:
  4092.                     index += 1
  4093.                     continue
  4094.             
  4095.             sortedLabels.insert(index, label)
  4096.         
  4097.         return sortedLabels
  4098.  
  4099.     
  4100.     def phoneticSpellCurrentItem(self, itemString):
  4101.         '''Phonetically spell the current flat review word or line.
  4102.  
  4103.         Arguments:
  4104.         - itemString: the string to phonetically spell.
  4105.         '''
  4106.         for charIndex, character in enumerate(itemString.decode('UTF-8')):
  4107.             if character.isupper():
  4108.                 voice = settings.voices[settings.UPPERCASE_VOICE]
  4109.                 character = character.lower()
  4110.             else:
  4111.                 voice = settings.voices[settings.DEFAULT_VOICE]
  4112.             phoneticString = phonnames.getPhoneticName(character)
  4113.             speech.speak(phoneticString, voice)
  4114.         
  4115.  
  4116.     
  4117.     def printAncestry(self, child):
  4118.         """Prints a hierarchical view of a child's ancestry."""
  4119.         if not child:
  4120.             return None
  4121.         ancestorList = [
  4122.             child]
  4123.         parent = child.parent
  4124.         while parent and parent.parent != parent:
  4125.             ancestorList.insert(0, parent)
  4126.             parent = parent.parent
  4127.             continue
  4128.             child
  4129.         indent = ''
  4130.         for ancestor in ancestorList:
  4131.             print indent + '+-', debug.getAccessibleDetails(ancestor)
  4132.             indent += '  '
  4133.         
  4134.  
  4135.     
  4136.     def printHierarchy(self, root, ooi, indent = '', onlyShowing = True, omitManaged = True):
  4137.         '''Prints the accessible hierarchy of all children
  4138.  
  4139.         Arguments:
  4140.         -indent:      Indentation string
  4141.         -root:        Accessible where to start
  4142.         -ooi:         Accessible object of interest
  4143.         -onlyShowing: If True, only show children painted on the screen
  4144.         -omitManaged: If True, omit children that are managed descendants
  4145.         '''
  4146.         if not root:
  4147.             return None
  4148.         if root == ooi:
  4149.             print indent + '(*)', debug.getAccessibleDetails(root)
  4150.         else:
  4151.             print indent + '+-', debug.getAccessibleDetails(root)
  4152.         rootManagesDescendants = root.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS)
  4153.         for child in root:
  4154.             if child == root:
  4155.                 print indent + '  ' + 'WARNING CHILD == PARENT!!!'
  4156.                 continue
  4157.             if not child:
  4158.                 print indent + '  ' + 'WARNING CHILD IS NONE!!!'
  4159.                 continue
  4160.             if child.parent != root:
  4161.                 print indent + '  ' + 'WARNING CHILD.PARENT != PARENT!!!'
  4162.                 continue
  4163.             if not onlyShowing and onlyShowing:
  4164.                 pass
  4165.             paint = child.getState().contains(pyatspi.STATE_SHOWING)
  4166.             if paint and not omitManaged and omitManaged:
  4167.                 pass
  4168.             paint = not rootManagesDescendants
  4169.             if paint:
  4170.                 self.printHierarchy(child, ooi, indent + '  ', onlyShowing, omitManaged)
  4171.                 continue
  4172.         
  4173.  
  4174.     
  4175.     def printApps(self):
  4176.         '''Prints a list of all applications to stdout.'''
  4177.         level = debug.LEVEL_OFF
  4178.         apps = self.getKnownApplications()
  4179.         debug.println(level, 'There are %d Accessible applications' % len(apps))
  4180.         for app in apps:
  4181.             debug.printDetails(level, '  App: ', app, False)
  4182.             for child in app:
  4183.                 debug.printDetails(level, '    Window: ', child, False)
  4184.                 if child.parent != app:
  4185.                     debug.println(level, "      WARNING: child's parent is not app!!!")
  4186.                     continue
  4187.             
  4188.         
  4189.         return True
  4190.  
  4191.     
  4192.     def printActiveApp(self):
  4193.         '''Prints the active application.'''
  4194.         level = debug.LEVEL_OFF
  4195.         window = self.findActiveWindow()
  4196.         if not window:
  4197.             debug.println(level, 'Active application: None')
  4198.         else:
  4199.             app = window.getApplication()
  4200.             if not app:
  4201.                 debug.println(level, 'Active application: None')
  4202.             else:
  4203.                 debug.println(level, 'Active application: %s' % app.name)
  4204.  
  4205.     
  4206.     def isInActiveApp(self, obj):
  4207.         '''Returns True if the given object is from the same application that
  4208.         currently has keyboard focus.
  4209.  
  4210.         Arguments:
  4211.         - obj: an Accessible object
  4212.         '''
  4213.         if not obj:
  4214.             return False
  4215.         if orca_state.locusOfFocus:
  4216.             pass
  4217.         return orca_state.locusOfFocus.getApplication() == obj.getApplication()
  4218.  
  4219.     
  4220.     def findActiveWindow(self):
  4221.         """Traverses the list of known apps looking for one who has an
  4222.         immediate child (i.e., a window) whose state includes the active state.
  4223.  
  4224.         Returns the Python Accessible of the window that's active or None if
  4225.         no windows are active.
  4226.         """
  4227.         window = None
  4228.         apps = self.getKnownApplications()
  4229.         for app in apps:
  4230.             for child in app:
  4231.                 
  4232.                 try:
  4233.                     state = child.getState()
  4234.                     if state.contains(pyatspi.STATE_ACTIVE):
  4235.                         window = child
  4236.                         break
  4237.                 continue
  4238.                 debug.printException(debug.LEVEL_FINEST)
  4239.                 continue
  4240.  
  4241.             
  4242.         
  4243.         return window
  4244.  
  4245.     
  4246.     def getAncestor(self, obj, ancestorRoles, stopRoles):
  4247.         '''Returns the object of the specified roles which contains the
  4248.         given object, or None if the given object is not contained within
  4249.         an object the specified roles.
  4250.  
  4251.         Arguments:
  4252.         - obj: the Accessible object
  4253.         - ancestorRoles: the list of roles to look for
  4254.         - stopRoles: the list of roles to stop the search at
  4255.         '''
  4256.         if not obj:
  4257.             return None
  4258.         if not isinstance(ancestorRoles, [].__class__):
  4259.             ancestorRoles = [
  4260.                 ancestorRoles]
  4261.         
  4262.         if not isinstance(stopRoles, [].__class__):
  4263.             stopRoles = [
  4264.                 stopRoles]
  4265.         
  4266.         ancestor = None
  4267.         obj = obj.parent
  4268.         while obj and obj != obj.parent:
  4269.             if obj.getRole() in ancestorRoles:
  4270.                 ancestor = obj
  4271.                 break
  4272.                 continue
  4273.             if obj.getRole() in stopRoles:
  4274.                 break
  4275.                 continue
  4276.             obj = obj.parent
  4277.         return ancestor
  4278.  
  4279.     
  4280.     def saveOldAppSettings(self):
  4281.         '''Save a copy of all the existing application specific settings
  4282.         (as specified by the settings.userCustomizableSettings dictionary).'''
  4283.         return orca_prefs.readPreferences()
  4284.  
  4285.     
  4286.     def restoreOldAppSettings(self, prefsDict):
  4287.         '''Restore a copy of all the previous saved application settings.
  4288.  
  4289.         Arguments:
  4290.         - prefsDict: the dictionary containing the old application settings.
  4291.         '''
  4292.         for key in settings.userCustomizableSettings:
  4293.             if key in prefsDict:
  4294.                 setattr(settings, key, prefsDict[key])
  4295.                 continue
  4296.         
  4297.  
  4298.     
  4299.     def drawOutline(self, x, y, width, height):
  4300.         '''Draws an outline around the accessible, erasing the
  4301.         last drawn outline in the process.'''
  4302.         if x == -1 and y == 0 and width == 0 and height == 0:
  4303.             outline.erase()
  4304.         else:
  4305.             outline.draw(x, y, width, height)
  4306.  
  4307.     
  4308.     def outlineAccessible(self, accessible):
  4309.         '''Draws a rectangular outline around the accessible, erasing the
  4310.         last drawn rectangle in the process.'''
  4311.         
  4312.         try:
  4313.             component = accessible.queryComponent()
  4314.         except AttributeError:
  4315.             self.drawOutline(-1, 0, 0, 0)
  4316.         except NotImplementedError:
  4317.             pass
  4318.  
  4319.         visibleRectangle = component.getExtents(pyatspi.DESKTOP_COORDS)
  4320.         self.drawOutline(visibleRectangle.x, visibleRectangle.y, visibleRectangle.width, visibleRectangle.height)
  4321.  
  4322.     
  4323.     def isTextSelected(self, obj, startOffset, endOffset):
  4324.         '''Returns an indication of whether the text is selected by
  4325.         comparing the text offset with the various selected regions of
  4326.         text for this accessible object.
  4327.  
  4328.         Arguments:
  4329.         - obj: the Accessible object.
  4330.         - startOffset: text start offset.
  4331.         - endOffset: text end offset.
  4332.  
  4333.         Returns an indication of whether the text is selected.
  4334.         '''
  4335.         if startOffset == endOffset:
  4336.             return False
  4337.         
  4338.         try:
  4339.             text = obj.queryText()
  4340.         except:
  4341.             startOffset == endOffset
  4342.             return False
  4343.  
  4344.         for i in xrange(text.getNSelections()):
  4345.             (startSelOffset, endSelOffset) = text.getSelection(i)
  4346.             if startOffset >= startSelOffset and endOffset <= endSelOffset:
  4347.                 return True
  4348.         
  4349.         return False
  4350.  
  4351.     
  4352.     def _saveSpokenTextRange(self, startOffset, endOffset):
  4353.         '''Save away the start and end offset of the range of text that
  4354.         was spoken. It will be used by speakTextSelectionState, to try
  4355.         to determine if the text was selected or unselected.
  4356.  
  4357.         Arguments:
  4358.         - startOffset: the start of the spoken text range.
  4359.         - endOffset: the end of the spoken text range.
  4360.         '''
  4361.         self.pointOfReference['spokenTextRange'] = [
  4362.             startOffset,
  4363.             endOffset]
  4364.  
  4365.     
  4366.     def _saveLastCursorPosition(self, obj, caretOffset):
  4367.         '''Save away the current text cursor position for next time.
  4368.  
  4369.         Arguments:
  4370.         - obj: the current accessible
  4371.         - caretOffset: the cursor position within this object
  4372.         '''
  4373.         self.pointOfReference['lastCursorPosition'] = [
  4374.             obj,
  4375.             caretOffset]
  4376.  
  4377.     
  4378.     def _saveLastTextSelections(self, text):
  4379.         '''Save away the list of text selections for next time.
  4380.  
  4381.         Arguments:
  4382.         - text: the text object.
  4383.         '''
  4384.         self.pointOfReference['lastSelections'] = []
  4385.         for i in xrange(text.getNSelections()):
  4386.             self.pointOfReference['lastSelections'].append(text.getSelection(i))
  4387.         
  4388.  
  4389.     
  4390.     def speakTextSelectionState(self, obj, startOffset, endOffset):
  4391.         '''Speak "selected" if the text was just selected, "unselected" if
  4392.         it was just unselected.
  4393.  
  4394.         Arguments:
  4395.         - obj: the Accessible object.
  4396.         - startOffset: text start offset.
  4397.         - endOffset: text end offset.
  4398.         '''
  4399.         
  4400.         try:
  4401.             text = obj.queryText()
  4402.         except:
  4403.             return None
  4404.  
  4405.         if isinstance(orca_state.lastInputEvent, input_event.KeyboardEvent):
  4406.             eventStr = orca_state.lastNonModifierKeyEvent.event_string
  4407.             mods = orca_state.lastInputEvent.modifiers
  4408.         else:
  4409.             eventStr = None
  4410.             mods = 0
  4411.         isControlKey = mods & settings.CTRL_MODIFIER_MASK
  4412.         isShiftKey = mods & settings.SHIFT_MODIFIER_MASK
  4413.         selectedText = text.getNSelections() != 0
  4414.         specialCaseFound = False
  4415.         if eventStr == 'Page_Down' and isShiftKey and isControlKey:
  4416.             specialCaseFound = True
  4417.             line = _('line selected to end from previous cursor position')
  4418.         elif eventStr == 'Page_Up' and isShiftKey and isControlKey:
  4419.             specialCaseFound = True
  4420.             line = _('line selected from start to previous cursor position')
  4421.         elif eventStr == 'Page_Down' and isShiftKey and not isControlKey:
  4422.             specialCaseFound = True
  4423.             if selectedText:
  4424.                 line = _('page selected from cursor position')
  4425.             else:
  4426.                 line = _('page unselected from cursor position')
  4427.         elif eventStr == 'Page_Up' and isShiftKey and not isControlKey:
  4428.             specialCaseFound = True
  4429.             if selectedText:
  4430.                 line = _('page selected to cursor position')
  4431.             else:
  4432.                 line = _('page unselected to cursor position')
  4433.         elif eventStr == 'Down' and isShiftKey and isControlKey:
  4434.             specialCaseFound = True
  4435.             if selectedText:
  4436.                 line = _('line selected down from cursor position')
  4437.             else:
  4438.                 line = _('line unselected down from cursor position')
  4439.         elif eventStr == 'Up' and isShiftKey and isControlKey:
  4440.             specialCaseFound = True
  4441.             if selectedText:
  4442.                 line = _('line selected up from cursor position')
  4443.             else:
  4444.                 line = _('line unselected up from cursor position')
  4445.         elif eventStr == 'Home' and isShiftKey and isControlKey:
  4446.             specialCaseFound = True
  4447.             if selectedText:
  4448.                 line = _('document selected to cursor position')
  4449.             else:
  4450.                 line = _('document unselected to cursor position')
  4451.         elif eventStr == 'End' and isShiftKey and isControlKey:
  4452.             specialCaseFound = True
  4453.             if selectedText:
  4454.                 line = _('document selected from cursor position')
  4455.             else:
  4456.                 line = _('document unselected from cursor position')
  4457.         elif eventStr == 'A' and isControlKey:
  4458.             charCount = text.characterCount
  4459.             for i in range(0, text.getNSelections()):
  4460.                 (startOffset, endOffset) = text.getSelection(i)
  4461.                 if text.caretOffset == 0 and startOffset == 0 and endOffset == charCount:
  4462.                     specialCaseFound = True
  4463.                     self.updateBraille(obj)
  4464.                     line = _('entire document selected')
  4465.                     continue
  4466.             
  4467.         
  4468.         if specialCaseFound:
  4469.             speech.speak(line, None, False)
  4470.             return None
  4471.         if startOffset == endOffset:
  4472.             return None
  4473.         
  4474.         try:
  4475.             
  4476.             try:
  4477.                 tmpStr = text.getText(startOffset, endOffset).decode('UTF-8')
  4478.             except:
  4479.                 startOffset == endOffset
  4480.                 specialCaseFound
  4481.                 tmpStr = u''
  4482.  
  4483.             n = len(tmpStr)
  4484.             if n > 1:
  4485.                 while endOffset > startOffset:
  4486.                     if self.isWordDelimiter(tmpStr[n - 1]):
  4487.                         n -= 1
  4488.                         endOffset -= 1
  4489.                         continue
  4490.                     startOffset == endOffset
  4491.                     break
  4492.                     continue
  4493.                     specialCaseFound
  4494.                 n = 0
  4495.                 while startOffset < endOffset:
  4496.                     if self.isWordDelimiter(tmpStr[n]):
  4497.                         n += 1
  4498.                         startOffset += 1
  4499.                         continue
  4500.                     break
  4501.         except:
  4502.             startOffset == endOffset
  4503.             specialCaseFound
  4504.             debug.printException(debug.LEVEL_FINEST)
  4505.  
  4506.         if self.isTextSelected(obj, startOffset, endOffset):
  4507.             speech.speak(C_('text', 'selected'), None, False)
  4508.         elif len(text.getText(startOffset, endOffset)):
  4509.             speech.speak(C_('text', 'unselected'), None, False)
  4510.         
  4511.         self._saveLastTextSelections(text)
  4512.  
  4513.     
  4514.     def getURI(self, obj):
  4515.         '''Return the URI for a given link object.
  4516.  
  4517.         Arguments:
  4518.         - obj: the Accessible object.
  4519.         '''
  4520.         return obj.queryHyperlink().getURI(0)
  4521.  
  4522.     
  4523.     def getDocumentFrame(self):
  4524.         '''Dummy method used as a reminder to refactor whereamI for links,
  4525.         possibly subclassing whereamI for the Gecko script.
  4526.         '''
  4527.         pass
  4528.  
  4529.     
  4530.     def systemBeep(self):
  4531.         '''Rings the system bell.  This is really a hack.  Ideally, we want
  4532.         a method that will present an earcon (any sound designated representing
  4533.         an error, event etc)
  4534.         '''
  4535.         print '\x07'
  4536.  
  4537.     
  4538.     def setCaretOffset(self, obj, offset):
  4539.         '''Set the caret offset on a given accessible. Similar to
  4540.         Accessible.setCaretOffset()
  4541.  
  4542.         Arguments:
  4543.         - obj: Given accessible object.
  4544.         - offset: Offset to hich to set the caret.
  4545.         '''
  4546.         
  4547.         try:
  4548.             texti = obj.queryText()
  4549.         except:
  4550.             return None
  4551.  
  4552.         texti.setCaretOffset(offset)
  4553.  
  4554.     
  4555.     def attribsToDictionary(self, dict_string):
  4556.         '''Creates a Python dict from a typical attributes list returned from
  4557.         different AT-SPI methods.
  4558.  
  4559.         Arguments:
  4560.         - dict_string: A list of colon seperated key/value pairs seperated by
  4561.         semicolons.
  4562.         Returns a Python dict of the given attributes.
  4563.         '''
  4564.         
  4565.         try:
  4566.             return dict(map((lambda pair: pair.strip().split(':')), dict_string.strip('; ').split(';')))
  4567.         except ValueError:
  4568.             return { }
  4569.  
  4570.  
  4571.     
  4572.     def _getPopupItemAtDesktopCoords(self, x, y):
  4573.         """Since pop-up items often don't contain nested components, we need
  4574.         a way to efficiently determine if the cursor is over a menu item.
  4575.  
  4576.         Arguments:
  4577.         - x: X coordinate.
  4578.         - y: Y coordinate.
  4579.  
  4580.         Returns a menu item the mouse is over, or None.
  4581.         """
  4582.         suspect_children = []
  4583.         if self.lastSelectedMenu:
  4584.             
  4585.             try:
  4586.                 si = self.lastSelectedMenu.querySelection()
  4587.             except NotImplementedError:
  4588.                 return None
  4589.  
  4590.             if si.nSelectedChildren > 0:
  4591.                 suspect_children = [
  4592.                     si.getSelectedChild(0)]
  4593.             else:
  4594.                 suspect_children = self.lastSelectedMenu
  4595.             for child in suspect_children:
  4596.                 
  4597.                 try:
  4598.                     ci = child.queryComponent()
  4599.                 except NotImplementedError:
  4600.                     continue
  4601.  
  4602.                 if ci.contains(x, y, pyatspi.DESKTOP_COORDS) and ci.getLayer() == pyatspi.LAYER_POPUP:
  4603.                     return child
  4604.             
  4605.         
  4606.  
  4607.     
  4608.     def getComponentAtDesktopCoords(self, parent, x, y):
  4609.         '''Get the descendant component at the given desktop coordinates.
  4610.  
  4611.         Arguments:
  4612.  
  4613.         - parent: The parent component we are searching below.
  4614.         - x: X coordinate.
  4615.         - y: Y coordinate.
  4616.  
  4617.         Returns end-node that contains the given coordinates, or None.
  4618.         '''
  4619.         acc = self._getPopupItemAtDesktopCoords(x, y)
  4620.         if acc:
  4621.             return acc
  4622.         container = parent
  4623.         while True:
  4624.             if container.getRole() == pyatspi.ROLE_PAGE_TAB_LIST:
  4625.                 
  4626.                 try:
  4627.                     si = container.querySelection()
  4628.                     container = si.getSelectedChild(0)[0]
  4629.                 except NotImplementedError:
  4630.                     acc
  4631.                     acc
  4632.                 except:
  4633.                     acc<EXCEPTION MATCH>NotImplementedError
  4634.                 
  4635.  
  4636.             acc
  4637.             
  4638.             try:
  4639.                 ci = container.queryComponent()
  4640.             except:
  4641.                 acc
  4642.                 return None
  4643.  
  4644.             inner_container = container
  4645.             container = ci.getAccessibleAtPoint(x, y, pyatspi.DESKTOP_COORDS)
  4646.             if not container or container.queryComponent() == ci:
  4647.                 break
  4648.                 continue
  4649.             acc
  4650.         if inner_container == parent:
  4651.             return None
  4652.         return inner_container
  4653.  
  4654.     
  4655.     def getTextSelections(self, acc):
  4656.         '''Get a list of text selections in the given accessible object,
  4657.         equivelent to getNSelections()*texti.getSelection()
  4658.  
  4659.         Arguments:
  4660.         - acc: An accessible.
  4661.  
  4662.         Returns list of start and end offsets for multiple selections, or an
  4663.         empty list if nothing is selected or if the accessible does not support
  4664.         the text interface.
  4665.         '''
  4666.         rv = []
  4667.         
  4668.         try:
  4669.             texti = acc.queryText()
  4670.         except:
  4671.             return rv
  4672.  
  4673.         for i in xrange(texti.getNSelections()):
  4674.             rv.append(texti.getSelection(i))
  4675.         
  4676.         return rv
  4677.  
  4678.     
  4679.     def speakWordUnderMouse(self, acc):
  4680.         '''Determine if the speak-word-under-mouse capability applies to
  4681.         the given accessible.
  4682.  
  4683.         Arguments:
  4684.         - acc: Accessible to test.
  4685.  
  4686.         Returns True if this accessible should provide the single word.
  4687.         '''
  4688.         if acc:
  4689.             pass
  4690.         return acc.getState().contains(pyatspi.STATE_EDITABLE)
  4691.  
  4692.     
  4693.     def getTextAttributes(self, acc, offset, get_defaults = False):
  4694.         """Get the text attributes run for a given offset in a given accessible
  4695.  
  4696.         Arguments:
  4697.         - acc: An accessible.
  4698.         - offset: Offset in the accessible's text for which to retrieve the
  4699.         attributes.
  4700.         - get_defaults: Get the default attributes as well as the unique ones.
  4701.         Default is True
  4702.  
  4703.         Returns a dictionary of attributes, a start offset where the attributes
  4704.         begin, and an end offset. Returns ({}, 0, 0) if the accessible does not
  4705.         supprt the text attribute.
  4706.         """
  4707.         rv = { }
  4708.         
  4709.         try:
  4710.             texti = acc.queryText()
  4711.         except:
  4712.             return (rv, 0, 0)
  4713.  
  4714.         if get_defaults:
  4715.             rv.update(self.attribsToDictionary(texti.getDefaultAttributes()))
  4716.         
  4717.         (attrib_str, start, end) = texti.getAttributes(offset)
  4718.         rv.update(self.attribsToDictionary(attrib_str))
  4719.         return (rv, start, end)
  4720.  
  4721.     
  4722.     def getWordAtCoords(self, acc, x, y):
  4723.         '''Get the word at the given coords in the accessible.
  4724.  
  4725.         Arguments:
  4726.         - acc: Accessible that supports the Text interface.
  4727.         - x: X coordinate.
  4728.         - y: Y coordinate.
  4729.  
  4730.         Returns a tuple containing the word, start offset, and end offset.
  4731.         '''
  4732.         
  4733.         try:
  4734.             ti = acc.queryText()
  4735.         except NotImplementedError:
  4736.             return ('', 0, 0)
  4737.  
  4738.         text_contents = ti.getText(0, -1)
  4739.         line_offsets = []
  4740.         start_offset = 0
  4741.         while True:
  4742.             
  4743.             try:
  4744.                 end_offset = text_contents.index('\n', start_offset)
  4745.             except ValueError:
  4746.                 line_offsets.append((start_offset, len(text_contents)))
  4747.                 break
  4748.  
  4749.             line_offsets.append((start_offset, end_offset))
  4750.             start_offset = end_offset + 1
  4751.         for start, end in line_offsets:
  4752.             (bx, by, bw, bh) = ti.getRangeExtents(start, end, pyatspi.DESKTOP_COORDS)
  4753.             bb = mouse_review.BoundingBox(bx, by, bw, bh)
  4754.             if bb.isInBox(x, y):
  4755.                 start_offset = 0
  4756.                 word_offsets = []
  4757.                 while True:
  4758.                     
  4759.                     try:
  4760.                         end_offset = text_contents[start:end].index(' ', start_offset)
  4761.                     except ValueError:
  4762.                         word_offsets.append((start_offset, len(text_contents[start:end])))
  4763.                         break
  4764.  
  4765.                     word_offsets.append((start_offset, end_offset))
  4766.                     start_offset = end_offset + 1
  4767.                 for a, b in word_offsets:
  4768.                     (bx, by, bw, bh) = ti.getRangeExtents(start + a, start + b, pyatspi.DESKTOP_COORDS)
  4769.                     bb = mouse_review.BoundingBox(bx, by, bw, bh)
  4770.                     if bb.isInBox(x, y):
  4771.                         return (text_contents[start + a:start + b], start + a, start + b)
  4772.                 
  4773.             bb.isInBox(x, y)
  4774.         
  4775.         return ('', 0, 0)
  4776.  
  4777.  
  4778. state_change_notifiers = { }
  4779. state_change_notifiers[pyatspi.ROLE_CHECK_MENU_ITEM] = ('checked', None)
  4780. state_change_notifiers[pyatspi.ROLE_CHECK_BOX] = ('checked', 'indeterminate', None)
  4781. state_change_notifiers[pyatspi.ROLE_PANEL] = ('showing', None)
  4782. state_change_notifiers[pyatspi.ROLE_LABEL] = ('showing', None)
  4783. state_change_notifiers[pyatspi.ROLE_RADIO_BUTTON] = ('checked', None)
  4784. state_change_notifiers[pyatspi.ROLE_TOGGLE_BUTTON] = ('checked', 'pressed', None)
  4785. state_change_notifiers[pyatspi.ROLE_TABLE_CELL] = ('checked', 'expanded', None)
  4786. state_change_notifiers[pyatspi.ROLE_LIST_ITEM] = ('expanded', None)
  4787.